How can I prevent one ArrayList changing another

Viewed 50

I have two identical ArrayLists, say l1 and l2.

Both are multi-dimensional, for instance ArrayList<ArrayList<Integer>>.

When I create and populate l1, I'm using new ArrayList<>(l1); to initialize l2. If these were ArrayLists of a single dimension, me editing l2 would leave l1 unchanged, but since they are not, adding a new Integer to l2 also changes l1. If I were to add a new ArrayList to it, though, l1 wouldn't do anything.

What can be done to make sure that changing an element in l2 doesn't tamper with l1?

Example:

ArrayList<ArrayList<Integer>> l1 = new ArrayList<>();

for (var x = 0; x < 10; x++) {
  l1.add(new ArrayList<>());

  for (var y = 0; y < 10; y++) {
    l1.get(x).add(y);
  }
}

ArrayList<ArrayList<Integer>> l2 = new ArrayList<ArrayList<Integer>>(l1);

//Should not change l1
l2.get(0).add(999);
3 Answers

When creating this two arrays. We are only creating two new Objects of ArrayList but not creating a new Instances of the Objects inside the List. So whatever is inside of this two ArrayLists if L1 make a change L2 will reflect the change because this two ArrayLists are referring the same Object in the Memory.

For make this work don't initialize like this way

ArrayList<ArrayList<Integer>> l2 = new ArrayList<ArrayList<Integer>>(l1);

Iterate over l1 and create new Objects.

Copy-constructors in the Collection framework always perform a shallow copy, not a deep copy, as you've probably expected.

Javadoc of the ArrayList(Collection<? extends E> c) says:

Constructs a list containing the elements of the specified collection, in the order they are returned by the collection's iterator.

Namely, the resulting list would contain exactly the same elements, not their copies. I.e. in your case, references the same inner lists.

Hence, you need to copy each element in the nested list manually:

public static <T> List<List<T>> copyNestedList(List<List<T>> list) {
    
    List<List<T>> copy = new ArrayList<>();
    for (List<T> l: list) {
        copy.add(new ArrayList<>(l));
    }
    return copy;
}

If you're comfortable with Stream API, you can do it like that:

public static <T> List<List<T>> copyNestedList(List<List<T>> list) {
    
    return list.stream()
        .map(ArrayList::new)
        .collect(Collectors.toList());
}

You could manually create a new list without referencing the original one

ArrayList<ArrayList<Integer>> l1 = new ArrayList<>();

    for (var x = 0; x < 2; x++) {
      l1.add(new ArrayList<>());
    
      for (var y = 0; y < 2; y++) {
        l1.get(x).add(y);
      }
    }
    
    ArrayList<ArrayList<Integer>> l2 = new ArrayList<ArrayList<Integer>>();
    for (int i = 0;i < l1.size(); i++){
        
        ArrayList<Integer> tmp = new ArrayList<Integer>();
        
        for (int j = 0;j < l1.get(i).size(); j++){
            
            tmp.add(l1.get(i).get(j));
        }  
        l2.add(tmp);
    } 
Related