使用removeAll移除列表子列表为何出现不同结果?
问题分析:两段ArrayList removeAll代码的差异原因
先看两段代码及运行结果:
第一段代码:
List<Integer> list = new ArrayList<>(); list.add(1); list.add(0); list.add(0); list.removeAll(list.subList(0, 1)); System.out.println(list);
运行结果:[0]
第二段代码:
List<Integer> list = new ArrayList<>(); list.add(1); list.add(0); list.add(1); list.removeAll(list.subList(0, 1)); System.out.println(list);
运行结果:[0, 1]
核心原因:subList是原列表的视图 + ArrayList batchRemove的实现逻辑
- subList的本质:
list.subList(0,1)返回的不是新列表,而是原ArrayList的视图,它直接引用原列表的底层数组,视图的内容会随原数组的修改实时变化。 - ArrayList.removeAll的实现逻辑:
removeAll内部调用batchRemove方法,核心逻辑是遍历原数组,把不需要移除的元素覆盖到数组前半部分,最后截断数组。关键在于:遍历过程中会直接修改原数组元素,subList的内容会同步变化,进而影响后续的元素判断。
第一段代码的详细执行过程
原列表初始底层数组:[1, 0, 0],size=3,subList(0,1)的内容是原数组索引0的元素1。
- r=0:当前元素是
1,调用subList.contains(1)返回true,因此不保留该元素,w(保留元素的指针)保持0,r自增到1。 - r=1:当前元素是
0,调用subList.contains(0)返回false(此时subList内容还是1),因此将该元素覆盖到数组索引0的位置,数组变为[0, 0, 0],w自增到1,r自增到2。 - r=2:当前元素是
0,此时subList的内容已经因为原数组的修改变成了0,调用subList.contains(0)返回true,因此不保留该元素,w保持1,r自增到3,循环结束。 - 最后截断数组,size设为1,最终列表为
[0]。
第二段代码的详细执行过程
原列表初始底层数组:[1, 0, 1],size=3,subList(0,1)的内容是1。
- r=0:当前元素是
1,subList.contains(1)返回true,不保留,w=0,r=1。 - r=1:当前元素是
0,subList.contains(0)返回false,覆盖到数组索引0的位置,数组变为[0, 0, 1],w=1,r=2。此时subList内容变为0。 - r=2:当前元素是
1,调用subList.contains(1)返回false(subList内容是0),因此将该元素覆盖到数组索引1的位置,数组变为[0, 1, 1],w=2,r=3,循环结束。 - 截断数组,size设为2,最终列表为
[0, 1]。
总结
两段代码的差异源于:removeAll遍历过程中修改原数组,导致subList的内容实时变化,进而影响后续元素是否被移除的判断。第一段代码中第二个0因为subList内容变为0而被判定为需要移除,第二段代码中的1因为subList内容变为0而被判定为不需要移除。
内容的提问来源于stack exchange,提问作者Suttonese
相关产品推荐
相关产品推荐

