欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 房产 > 建筑 > subList与原始List相互影响

subList与原始List相互影响

2025/2/23 7:07:15 来源:https://blog.csdn.net/weixin_63172268/article/details/144457884  浏览:    关键词:subList与原始List相互影响

在 Java 中,List#subList(int fromIndex, int toIndex) 方法返回的是原始列表的一个视图(view),而不是一个独立的副本。这意味着对 subList 的任何修改都会反映到原始列表中,反之亦然。这可能会导致意外的行为,尤其是在你期望 subList 是一个独立的列表时。

示例:

package org.example.a;import java.util.ArrayList;
import java.util.List;public class Demo {public static void main(String[] args) {List<Integer> integerList = new ArrayList<>();integerList.add(1);integerList.add(2);integerList.add(3);// 获取子列表List<Integer> subList = integerList.subList(0, 2);// 修改子列表中的元素subList.set(0, 10);// 修改原始列表中的元素integerList.set(1, 20);System.out.println("integerList: " + integerList);System.out.println("subList: " + subList);}
}

运行结果:

integerList: [10, 20, 3]

subList: [10, 20]

解释:

        修改 subList :当你调用 subList.set(0, 10) 时,实际上是在修改原始列表 integerList 的第一个元素,因此 integerList 的第一个元素变成了 10

        修改 integerList :当你调用 integerList.set(1, 20) 时,integerList 的第二个元素变成了 20,同时这个变化也会反映到 subList 中,因为 subList 只是 integerList 的一个视图。

源码

SubList 的工作原理:subList 方法

public List<E> subList(int fromIndex, int toIndex) {subListRangeCheck(fromIndex, toIndex, size);return new SubList(this, 0, fromIndex, toIndex);
}

参数检查:首先调用 subListRangeCheck 方法确保索引范围有效。

返回 SubList 示例:然后创建并返回一个新的 SubList 对象,该对象持有对原始 ArrayList 的引用,并记录了子列表的起始和结束位置。

SubList 类

private class SubList extends AbstractList<E> implements RandomAccess {private final AbstractList<E> parent;private final int parentOffset;private final int offset;int size;SubList(AbstractList<E> parent,int offset, int fromIndex, int toIndex) {this.parent = parent;this.parentOffset = fromIndex;this.offset = offset + fromIndex;this.size = toIndex - fromIndex;this.modCount = ArrayList.this.modCount;}public void add(int index, E e) {rangeCheckForAdd(index);checkForComodification();parent.add(parentOffset + index, e);this.modCount = parent.modCount;this.size++;}
}

parent 字段:保存对原始 ArrayList 的引用。

parentOffset 和 offset 字段:记录子列表相对于原始列表的偏移量。

size 字段:表示子列表的大小。

add 方法:当向 SubList 添加元素时,实际上是通过 parent.add(parentOffset + index, e) 来操作原始列表。这意味着添加到 SubList 的元素会直接反映在原始列表中。

潜在问题

由于 SubList 实际上只是原始列表的一个视图,因此存在以下潜在问题:

        1. 相互影响:对 SubList 或原始列表的任何修改都会影响到对方,因为它们共享相同的底层数据结构。

        2. 内存泄漏:如果 SubList 被保留而原始列表很大,则会导致原始列表无法被垃圾回收,即使不再需要整个列表。这可能会导致内存溢出(OOM)问题,特别是在大量创建小 SubList 的情况下。

示例

private static List<List<Integer>> data = new ArrayList<>();private static void oom() {for (int i = 0; i < 1000; i++) {List<Integer> rawList = IntStream.rangeClosed(1, 100000).boxed().collect(Collectors.toList());data.add(rawList.subList(0, 1));}
}

虽然 data 看起来只保存了 1000 个具有 1 个元素的子列表,但实际上每个 SubList 都强引用了整个 rawList,导致 GC 无法回收这些大列表,最终可能导致内存溢出(OOM)。

解决方案

        为了确保 subList 和原始列表之间没有相互影响,可以创建一个新的 ArrayList 来包含 subList 的内容。这样做会创建一个独立的副本,从而避免上述问题。

package org.example.a;import java.util.ArrayList;
import java.util.List;public class Demo {public static void main(String[] args) {List<Integer> integerList = new ArrayList<>();integerList.add(1);integerList.add(2);integerList.add(3);// 创建新的 ArrayList 包含 subList 的内容List<Integer> subList = new ArrayList<>(integerList.subList(0, 2));// 修改子列表中的元素subList.set(0, 10);// 修改原始列表中的元素integerList.set(1, 20);System.out.println("integerList: " + integerList);System.out.println("subList: " + subList);}
}

运行结果:

integerList: [1, 20, 3]

subList: [10, 2]

解释:

        修改 subList :由于 subList 现在是一个独立的 ArrayList,对它的修改不会影响到原始列表 integerList

        修改 integerList :同样地,对 integerList 的修改也不会影响到 subList,因为它们现在是完全独立的。

总结

subList 是原始列表的一个视图:它不是独立的副本,而是原始列表的一部分。因此,任何对 subList 或原始列表的修改都会相互影响。

内存管理问题:由于 subList 强引用了原始列表,可能导致不必要的内存占用,甚至引发 内存溢出(OOM) 错误。

解决方案:如果你需要一个真正独立的子列表,应该使用 new ArrayList<>(list.subList(...)) 来创建一个新的 ArrayList 包含 subList 的内容。这将确保两个列表之间的操作不会相互影响,并且避免潜在的内存问题。

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词