39

The following code outputs

[[100, 200, 300], [100, 200, 300]]. 

However, what I expect is

[[100, 200, 300], [100, 200]], 

Where am I wrong?

public static void main(String[] args) {
    ArrayList<ArrayList<Integer>> outer = new ArrayList<ArrayList<Integer>>();
    ArrayList<Integer> inner = new ArrayList<Integer>();        

    inner.add(100);     
    inner.add(200);

    outer.add(inner);
    outer.add(inner);

    outer.get(0).add(300);

    System.out.println(outer);

}
Martin G
  • 17,357
  • 9
  • 82
  • 98
Jie
  • 391
  • 2
  • 4
  • 5

4 Answers4

55

You are adding a reference to the same inner ArrayList twice to the outer list. Therefore, when you are changing the inner list (by adding 300), you see it in "both" inner lists (when actually there's just one inner list for which two references are stored in the outer list).

To get your desired result, you should create a new inner list :

public static void main(String[] args) {
    ArrayList<ArrayList<Integer>> outer = new ArrayList<ArrayList<Integer>>();
    ArrayList<Integer> inner = new ArrayList<Integer>();        

    inner.add(100);     
    inner.add(200);
    outer.add(inner); // add first list
    inner = new ArrayList<Integer>(inner); // create a new inner list that has the same content as  
                                           // the original inner list
    outer.add(inner); // add second list

    outer.get(0).add(300); // changes only the first inner list

    System.out.println(outer);
}
Eran
  • 387,369
  • 54
  • 702
  • 768
51

Before reading this answer you may want to familiarize yourself with What is the difference between a variable, object, and reference?


This is what you have now

ArrayList<ArrayList<Integer>> outer = new ArrayList<ArrayList<Integer>>();
ArrayList<Integer> inner = new ArrayList<Integer>();        

will create

outer -> []
inner -> []

which creates two separate lists and stores reference to them in variables outer and inner.


After

inner.add(100);     
inner.add(200);

your situation looks like

outer -> []
inner -> [100, 200]

Here comes confusing part

outer.add(inner);
outer.add(inner);

Here value of inner variable (so reference to list [100, 200]) is placed in outer list two times. This means that outer "points to" [100, 200] list two times

//note: `reference1` == `reference2` (like 42 == 42) since they point to same object
outer -> [ reference1, reference2 ] 
              |             |
      +-------+             |
      +---------------------+
      ↓
inner +-> [100, 200]

This means that if you change state of list [100, 200] you will be able to see these changes using outer.get(0) or outer.get(1) or inner, since all of them are reference to same list.

So if we use

outer.get(0).add(300);

the outer.get(0) returns reference to same list as inner and add(300) adds new element to that list. Which means that after it new situation will look like

outer -> [ reference1 , reference2 ]
              |             |
      +-------+             |
      +---------------------+
      ↓
inner -> [100, 200, 300]

That is why when you print outer you are seeing

[[100, 200, 300], [100, 200, 300]]. 
 ^^^^^^^^^^^^^^^  ^^^^^^^^^^^^^^^
   from get(0)      from get(1)

What you actually need is to create separate list so reference1 and reference2 will point to two separate lists. Something like

outer  -> []
inner1 -> [100, 200]
inner2 -> [100, 200]

which will be later organized into

outer -> [ reference1 , reference2 ]
              |             |
       +------+             |
       ↓                    |
inner1 -> [100, 200]        |
                            |
       +--------------------+
       ↓
inner2 -> [100, 200]

You can do it this way

List<List<Integer>> outer = new ArrayList<List<Integer>>();
List<Integer> inner1 = new ArrayList<Integer>();
List<Integer> inner2 = new ArrayList<Integer>();

inner1.add(100);
inner1.add(200);

inner2.add(100);
inner2.add(200);

outer.add(inner1);
outer.add(inner2);

outer.get(0).add(300);

System.out.println(outer);
//Output: [[100, 200, 300], [100, 200]]
Pshemo
  • 122,468
  • 25
  • 185
  • 269
10

The command outer.add(inner) adds a reference to inner, not a copy of it.

So, when you add two references to inner to the ArrayList outer, you're adding two of the same thing. Modifying inner through outer.get(0) also modifies the value in outer.get(1), because they refer to the same thing.

If you create a copy of inner and use that instead, then you'll have two different instances and be able to modify them separately. You can do this with a simple command:

outer.add(new ArrayList<[var type]>(inner));

The instruction for new ArrayList(inner) creates a new ArrayList with the contents of inner inside of it - but doesn't use the same instance as inner. Thus, you'll retain the content, but not retain the duplicated reference.

By adding the new copy instead of the reference, you can modify the copy without modifying what you might call the "original."

0

Try this example in IDE:

ArrayList<ArrayList<String>> ext = new ArrayList<ArrayList<String>>();
    String[] arr = {"1","2","3","4","5","6"};
    int n=arr.length;
    for(int i=0;i<n;i++){
        ArrayList temp = new ArrayList<String>();
        for(int j=i;j<n;j++){
            temp.add(arr[j]);
            // this the line that needs to look at. Rather than pointing to the same memory reference creating a new ArrayList will store it in a different memory location. If you just add temp then it will point to same memory location.
            ext.add(new ArrayList<String>(temp));
             // Comment above line and uncomment below to see the difference    
            //ext.add(temp);
        }

    }
    System.out.println(ext);
Vayuj Rajan
  • 85
  • 1
  • 2