Java列表使用异常:添加子列表后元素值意外变动问题排查
问题:List<List>中子列表值意外变动的原因分析
问题现象
向List<Integer>添加元素后将其加入List<List<Integer>>,但子列表的值在添加后立即发生意外变动。使用普通for循环替代foreach问题依旧,仅当在result.add()中通过new ArrayList<>(o)初始化新列表时,问题消失。
相关代码片段
List<List<Integer>> c2 = sum(rs, target - i); System.out.println(c2); for (List<Integer> o : c2) { System.out.println(result); result.add(o); result.get(result.size()-1).add(i); System.out.println(result); }
输出对比
原始输出(直接add(o))
[[1]] [] [[1, 1]] [[1]]
修改后输出(使用new ArrayList<>(o))
[[1]] [] [[1, 1]] [[1, 1]]
底层原因
核心问题在于Java中对象的引用传递机制:
- 当执行
result.add(o)时,并没有复制o列表的内容,而是将o的内存引用直接存入result。这意味着result中的子列表和c2中的o指向的是同一个List对象。 - 后续调用
result.get(result.size()-1).add(i)时,本质是在修改这个共享的List对象。如果该对象还被其他地方(比如递归栈中的返回结果、原始输入列表)持有引用,那么其他地方对该对象的修改也会同步反映到result中,导致你看到的“意外变动”。 - 再看递归的base case代码:
result.add(candidates);这里直接将传入的candidates列表引用加入结果,而candidates可能是上层方法中rs的原始引用(rs是candidates的副本,但base case中的candidates是rs本身),后续上层循环对rs的操作或其他递归调用可能间接修改这个共享对象,进一步加剧了值的异常变动。 - 当改用
result.add(new ArrayList<>(o))时,会创建一个全新的List对象,并复制o中的所有元素。此时result中的子列表是独立的新对象,和原来的o没有引用关联,修改新对象不会影响其他地方的列表,问题自然解决。
完整代码
import java.util.*; import java.util.stream.*; public class Solution { public static void main(String[] args) { (new Solution()).combinationSum2(new int[]{1,1}, 2); } public List<List<Integer>> combinationSum2(int[] candidates, int target) { List<Integer> s = IntStream.of(candidates).boxed().map(i -> i).sorted().collect(Collectors.toList()); return sum(s, target); } private List<List<Integer>> sum(List<Integer> candidates, int target) { List<List<Integer>> result = new ArrayList<>(); if (candidates.size() == 1) { if (candidates.get(0) == target) { System.out.println("found as single element"); result.add(candidates); } return result; } Integer last = null; List<Integer> rs = new ArrayList<>(candidates); for (int i : candidates) { rs.remove(0); if (last != null && last == i) continue; if (i == target) { List<Integer> tmp = new ArrayList<>(1); tmp.add(target); result.add(tmp); } List<List<Integer>> c2 = sum(rs, target - i); System.out.println(c2); for (List<Integer> o : c2) { System.out.println(result); result.add(o); result.get(result.size()-1).add(i); System.out.println(result); } last = i; } System.out.println(result); return result; } }
内容的提问来源于stack exchange,提问作者A. Johnston
相关产品推荐
相关产品推荐

