前两天在刷题的时候遇到个有趣的BUG,提醒了自己还是基础不牢固,这里记录一下
错误代码是这样的
1 public void backtrack(int n, List<Integer> output, List<List<Integer>> res, int first) { 2 if (first == n) { 3 res.add(output); 4 } 5 for (int i = first; i < n; i++) { 6 Collections.swap(output, first, i); 7 backtrack(n, output, res, first + 1); 8 Collections.swap(output, first, i); 9 } 10 }
这是一段回溯的代码,我们不断的修改集合output的值,并
利用res记录下来每一种结果。
但是最后发现res中的结果全部一致,比对了正确答案发现应该写成
res.add(new ArrayList<Integer>(output));
于是我又去查阅了下相关资料,发现其实我们很容易忽略集合本身也是对象
ArrayList的定义
public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable
集合中存储的是对象本身而非引用,上面的错误代码里res里存储的是output这个引用指向的对象。
output引用指向的对象一直是固定的,因此造成了实际上的对象内容的变化,最终造成的效果是res虽然
看起来压入了不同状态下的output,但由于他们都是同一个对象,因此最后res的结果全部是一致的。
这里给出一组参考样例
1 public static void main(String[] args){ 2 List<Integer> l1 = new ArrayList<Integer>(); 3 List<List<Integer>> l2 = new ArrayList<List<Integer>>(); 4 Integer a = 1; 5 l1.add(a); 6 l2.add(l1); 7 System.out.println(l1); 8 System.out.println(l2); 9 // 引用a指向了新的对象,旧的对象并未变化,因此l1未变 10 a = 2; 11 System.out.println(l1); 12 // 引用l1还是指向原来的对象,该对象产生了变化,因此l2变了 13 l1.add(a); 14 System.out.println(l2); 15 }
原文:https://www.cnblogs.com/jchen104/p/14899774.html