Christian 30
Christian 30

Reputation: 25

Store an ArrayList

I'm creating a program to automatize the train logical system, using Java. I want store trains with ArrayList:

ArrayList<Treni> train = new ArrayList<>();

and add a multiple stations to the train list:

ArrayList<Fermata> stop= new ArrayList<>();
stop.add(new Stop("Milan", "7", new DepartureTime((byte) 19, (byte) 35)));
train.add(new Train(87569, "Trenitalia", fermata));

Now i want to clear all stops to add a new stops:

stop.clear();

but with this method it clear the stops in entire class Train. How can i store the ArrayList stop and use the method clear(); without lose stops in the Train class?

This is Train.java:


public class Train{
  private int number;
  private String owner;
  private ArrayList<Stop> stop = new ArrayList<>();

  Train(int number, String owner, ArrayList<Fermata> stop) {
    this.number = number;
    this.owner = owner;
    this.stop = stop;
  }
}

class Stop{
  String station, binary;
  DepartureTime departure;
  ArrivalTime arrival;

  Stop(String station, String binary, DepartureTime departure, ArrivalTime arrival) {
    this.station= station;
    this.binary= binary;
    this.departure= departure;
    this.arrival= arrival;
  }

  Stop(String station, String binary, DepartureTime departure) {
    this.station = station;
    this.binary= binary;
    this.departure= departure;
  }
}

class DepartureTime{
  private byte hour, minute;

  DepartureTime(byte hour, byte minute) {
    this.hour= hour;
    this.minute = minute;
  }

}

class ArrivalTime {
  private byte hour, minute;

  ArrivalTime (byte hour, byte minute) {
    this.hour= hour;
    this.minute = minute;
  }
}```

Upvotes: 1

Views: 202

Answers (3)

Basil Bourque
Basil Bourque

Reputation: 338574

The Answer by Zidek is correct, and should be accepted. Here I will give some example code, while rewriting for brevity.

You can define your classes more briefly as records. Records are appropriate for classes whose main purpose is to communicate data transparently and immutably.

You can use List.of to instantiate an unmodifiable List object.

Rather than invent your own type for time-of-day, use java.time.LocalTime.

By the way, a train number should be of type String (or a custom class), not an integer type. The train number is really a train identifier, not a quantity nor an ordinal.

This code is untested, written in an iPhone, so may need some editing.

public record Train( int number, String owner, List<Stop> stops ) {}
public record Stop( String station, String binary, LocalTime departure, LocalTime arrival ) {}
List< Train > trains = 
    List.of( 
        new Train(
            87569, 
            "Trenitalia", 
            List.of( 
                new Stop( "Milan", "7", LocalTime.of( 19 , 35 ), null ) ,
                new Stop( "Venice", "7", LocalTime.of( 20 , 55 ) , null ) 
            )
        ) ,
        … new Train( … )
    );

If you need a list to be modifiable, instantiate ArrayList. But note that you can pass to its constructor a list produced by List.of. This technique makes for compact readable literals-style syntax.

List< Person > persons = 
    new ArrayList <>(
        List.of(
            new Person( "Alice" ) , 
            new Person( "Bob" ) ,
            new Person( "Carol" ) 
        )
    )
;

Upvotes: 2

tquadrat
tquadrat

Reputation: 4034

You fell in a well known Java trap: the argument stop of the constructor Train(int,String,List<Stop>) is not an (instance of an) ArrayList, it is just the reference to such an instance.

This means that the statement

this.stop = stop;

in that constructor does not keep an instance of that List, but just a reference.

As a consequence you should have noticed that all trains would have exactly the same list of stops when you do omit the call to stop.clear().

You now have two options to fix this:

  1. Instead of calling clear on the stop list, you create a new instance, by calling new ArrayList<Stop>.

  2. The recommended approach (as the first one is far from being bullet proof …) looks like this:

public class Train
{
  private final int number;
  private final String owner;
  private final List<Stop> stop;

  Train( final int number, final String owner, final List<Stop> stop ) 
  {
    this.number = number;
    this.owner = owner;
    this.stop = List.copyOf( stop );
  }
}

If you want to manipulate the stops later, you can also write

this.stop = new ArrayList<>( stop );

Copied from the closed duplicate of this question.

Upvotes: 0

Honza Zidek
Honza Zidek

Reputation: 20026

The solution is suggested in the @f1sh's and @MCEmperor's comments.

For some reasons, there is a meme among the programmers, that creating a new object is always more costly than recycling an old one. This is what probably misleads you to use stop.clear() instead of stop = new ArrayList<Stop>.

By the way, it's a good practice to name list variables in plural, so stops would be a much better name than stop for a list of stops.

There is probably other misunderstanding in your question. The parameters in Java are always passed as "reference by value". So what you are passing to your train is a reference to a list. Your Train object does not receive a copy of the list of stops, but just a reference to its memory representation in heap. So if you later change the content of the memory, your train refers to the new data.

On the other side, when you change the outside stops variable, the original memory will remain the same.

So just do as @f1sh says: "just create a new List instead of clearing the old one: stops = new ArrayList<>();"

Upvotes: 2

Related Questions