Categories
java

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

98

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.

0

    168

    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);
    }
    

    6

    • 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, 2014 at 9: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.

      Dec 10, 2014 at 9: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, 2014 at 9:33


    • 1

      @Dev Yup, that’s exactly it.

      Dec 10, 2014 at 9: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, 2019 at 9:51


    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.

    4

    • What do you mean by “type static“? What would be a non-static class to you?

      – 4castle

      Feb 3, 2017 at 3: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 4, 2017 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 4, 2017 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, 2021 at 11:03


    6

    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);
    
    }
    

    1

    • 1

      To future readers: you shouldn’t use Calendar at all.

      Mar 30, 2021 at 13:44