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.
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!
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.
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.
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.
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
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.
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); }
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With