102

I'm adding three different objects to an ArrayList, but the list contains three copies of the last object I added.

For example:

for (Foo f : list) {
  System.out.println(f.getValue());
}    

Expected:

0
1
2

Actual:

2
2
2

What mistake have I made?

Note: this is designed to be a canonical Q&A for the numerous similar issues that arise on this site.

Braiam
  • 1
  • 11
  • 47
  • 78
Duncan Jones
  • 67,400
  • 29
  • 193
  • 254

5 Answers5

170

This problem has two typical causes:

  • Static fields used by the objects you stored in the list

  • Accidentally adding the same object to the list

Static Fields

If the objects in your list store data in static fields, each object in your list will appear to be the same because they hold the same values. Consider the class below:

public class Foo {
  private static int value; 
  //      ^^^^^^------------ - Here's the problem!
  
  public Foo(int value) {
    this.value = value;
  }
  
  public int getValue() {
    return value;
  }
}

In that example, there is only one int value which is shared between all instances of Foo because it is declared static. (See "Understanding Class Members" tutorial.)

If you add multiple Foo objects to a list using the code below, each instance will return 3 from a call to getValue():

for (int i = 0; i < 4; i++) {      
  list.add(new Foo(i));
}

The solution is simple - don't use the static keywords for fields in your class unless you actually want the values shared between every instance of that class.

Adding the Same Object

If you add a temporary variable to a list, you must create a new instance of the object you are adding, each time you loop. Consider the following erroneous code snippet:

List<Foo> list = new ArrayList<Foo>();    
Foo tmp = new Foo();

for (int i = 0; i < 3; i++) {
  tmp.setValue(i);
  list.add(tmp);
}

Here, the tmp object was constructed outside the loop. As a result, the same object instance is being added to the list three times. The instance will hold the value 2, because that was the value passed during the last call to setValue().

To fix this, just move the object construction inside the loop:

List<Foo> list = new ArrayList<Foo>();        

for (int i = 0; i < 3; i++) {
  Foo tmp = new Foo(); // <-- fresh instance!
  tmp.setValue(i);
  list.add(tmp);
}
Community
  • 1
  • 1
Duncan Jones
  • 67,400
  • 29
  • 193
  • 254
  • 1
    hello @Duncan nice solution, i want to ask you that in "Adding the Same Object" why three different instance will hold the value 2 ,aren't all three instance should hold 3 different values? hope you will reply soon, thanks – Dev Dec 10 '14 at 09:20
  • 3
    @Dev Because the same object (`tmp`) is added to the list three times. And that object has a value of two, because of the call to `tmp.setValue(2)` in the final iteration of the loop. – Duncan Jones Dec 10 '14 at 09:22
  • 1
    ok so here problem is because of same object , Is it so if i add same object three times in a array list ,all three location of arraylist obj will refer to same object? – Dev Dec 10 '14 at 09:33
  • 1
    @Dev Yup, that's exactly it. – Duncan Jones Dec 10 '14 at 09:34
  • 1
    An obvious fact I initially overlooked: concerning the part `you must create a new instance each time you loop` in the section `Adding the same object`: Please note the instance referred to concerns the object you are adding, NOT the object you are adding it to. – a.t. May 28 '19 at 09:51
  • dear @DuncanJones (about Adding the Same Object) I didn't get the "reason". at least values of this list must be : [0,0,0] not [2,2,2]. also the problem is somewhere else where there is no constructor!!! see my question: [link](https://stackoverflow.com/questions/68507969/declaring-var-in-out-of-a-loops) – Mohammad_Hosein Jul 24 '21 at 08:32
7

Your problem is with the type static which requires a new initialization every time a loop is iterated. If you are in a loop it is better to keep the concrete initialization inside the loop.

List<Object> objects = new ArrayList<>(); 

for (int i = 0; i < length_you_want; i++) {
    SomeStaticClass myStaticObject = new SomeStaticClass();
    myStaticObject.tag = i;
    // Do stuff with myStaticObject
    objects.add(myStaticClass);
}

Instead of:

List<Object> objects = new ArrayList<>(); 

SomeStaticClass myStaticObject = new SomeStaticClass();
for (int i = 0; i < length; i++) {
    myStaticObject.tag = i;
    // Do stuff with myStaticObject
    objects.add(myStaticClass);
    // This will duplicate the last item "length" times
}

Here tag is a variable in SomeStaticClass to check the validity of the above snippet; you can have some other implementation based on your use case.

4castle
  • 32,613
  • 11
  • 69
  • 106
Shashank
  • 183
  • 1
  • 4
  • What do you mean by "type `static`"? What would be a non-static class to you? – 4castle Feb 03 '17 at 03:55
  • e.g. non-static: `public class SomeClass{/*some code*/}` & static: `public static class SomeStaticClass{/*some code*/}`. I hope it is clearer now. – Shashank Feb 04 '17 at 11:37
  • 1
    Since, all objects of a static class share same address, if they are initialized in a loop and are set with different values in each iteration. All of them will end up having identical value which will equal to the value of the last iteration or when the last object was modified. I hope it is clearer now. – Shashank Feb 04 '17 at 11:46
  • *Since, all objects of a static class share same address,* -> this is completely wrong in the context of Java. Pure `static` classes don't exist in java, you can add `static` to a nested class but that doesn't make it automatically a *single instance / identical reference*... – Lino Sep 28 '21 at 11:03
7

Had the same trouble with the calendar instance.

Wrong code:

Calendar myCalendar = Calendar.getInstance();

for (int days = 0; days < daysPerWeek; days++) {
    myCalendar.add(Calendar.DAY_OF_YEAR, 1);

    // In the next line lies the error
    Calendar newCal = myCalendar;
    calendarList.add(newCal);
}

You have to create a NEW object of the calendar, which can be done with calendar.clone();

Calendar myCalendar = Calendar.getInstance();

for (int days = 0; days < daysPerWeek; days++) {
    myCalendar.add(Calendar.DAY_OF_YEAR, 1);

    // RIGHT WAY
    Calendar newCal = (Calendar) myCalendar.clone();
    calendarList.add(newCal);

}
basti12354
  • 2,490
  • 4
  • 24
  • 43
5

Every time you add an object to an ArrayList, make sure you add a new object and not already used object. What is happening is that when you add the same 1 copy of object, that same object is added to different positions in an ArrayList. And when you make change to one, because the same copy is added over and over again, all the copies get affected. For example, Say you have an ArrayList like this:

ArrayList<Card> list = new ArrayList<Card>();
Card c = new Card();

Now if you add this Card c to list, it will be added no problem. It will be saved at location 0. But, when you save the same Card c in the list, it will be saved at location 1. So remember that you added same 1 object to two different locations in a list. Now if you make a change that Card object c, the objects in a list at location 0 and 1 will also reflect that change, because they are the same object.

One solution would be to make a constructor in Card class, that accepts another Card object. Then in that constructor, you can set the properties like this:

public Card(Card c){
this.property1 = c.getProperty1();
this.property2 = c.getProperty2(); 
... //add all the properties that you have in this class Card this way
}

And lets say you have the same 1 copy of Card, so at the time of adding a new object, you can do this:

list.add(new Card(nameOfTheCardObjectThatYouWantADifferentCopyOf));
Faraz
  • 6,025
  • 5
  • 31
  • 88
  • dear @Faraz this is a bug isn't it? java programmers must correct this I think. there is another kind of this problem in my question [here (specially, see final lines of question).](https://stackoverflow.com/questions/68507969/is-it-a-bug-why-lists-have-problem-with-some-kind-of-objects) – Mohammad_Hosein Jul 24 '21 at 10:17
  • the problem is with "some" kind of objects! not all. for example Strings have no problem. while Array of Strings have problem! – Mohammad_Hosein Jul 24 '21 at 10:21
  • 1
    My dear brother Mohammad Hosein, Assalam o Alaikum. The reason that Strings don't have that problem is because, Strings are immutable. Every time you modify a String, internally it creates a new String and returns it. That's why you don't see that problem with Strings. – Faraz Jul 24 '21 at 11:55
  • 1
    I have to go back to sleep. When I wake up I will try to look at your question. – Faraz Jul 24 '21 at 11:57
  • va alaikom alsalaam and thanks very much brother @Faraz. have a nice sleep. I got my answer (specially with this answer of you). only I want a help/tutorial for these referencing/linking... to avoid further problems. (I don't know what should I search in google?) thanks – Mohammad_Hosein Jul 24 '21 at 12:15
2

It can also consequence of using the same reference instead of using a new one.

 List<Foo> list = new ArrayList<Foo>();        

 setdata();
......

public void setdata(int i) {
  Foo temp = new Foo();
  tmp.setValue(i);
  list.add(tmp);
}

Instead of:

List<Foo> list = new ArrayList<Foo>(); 
Foo temp = new Foo();       
setdata();
......

public void setdata(int i) {
  tmp.setValue(i);
  list.add(tmp);
} 
SHR
  • 7,940
  • 9
  • 38
  • 57
Shaikh Mohib
  • 278
  • 3
  • 11