Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does my ArrayList contain N copies of the last item added to the list?

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.

like image 985
Duncan Jones Avatar asked Nov 07 '13 18:11

Duncan Jones


People also ask

Why does adding a new value to list overwrite previous values in the list?

Essentially, you're setting a Tag's name to the first value in tagList and adding it to the collection, then you're changing that same Tag's name to the second value in tagList and adding it again to the collection. Your collection of Tags contains several references to the same Tag object!

Can an ArrayList contain multiple references to the same object?

An ArrayList can contain multiple references to the same object. The same object may belong to 2 different ArrayLists. ArrayList's add method makes a copy of the object and adds it to the list. Two variables can refer to the same Arraylist.

Does ArrayList preserve order of insertion?

ArrayList maintains the insertion order i.e order of the object in which they are inserted. HashSet is an unordered collection and doesn't maintain any order. ArrayList allows duplicate values in its collection.

What happens when ArrayList full?

So the backing array is not growing, every time when it is full, The ArrayList class is creating a new array of bigger size and copies all the elements from the old array to the new array.


2 Answers

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); } 
like image 114
7 revs, 6 users 92% Avatar answered Oct 17 '22 12:10

7 revs, 6 users 92%


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.

like image 43
Shashank Avatar answered Oct 17 '22 11:10

Shashank