内存分页会用到sublist,业务里面比较常用的场景就是多线程跑批,需要先拆分任务。
文章目录
注意事项(demo)
- 结论:sublist获取的是原列表的镜像;改子列表不会报错、但会影响原列表;改原列表会导致子列表报错,子列表依赖原列表
public static void main(String[] args) {
List<String> list = new ArrayList<>(Arrays.asList("A", "B", "C", "D"));
List<String> subList = list.subList(1, 3); //[1, 3)
log.info("=== 刚截取的");
log.info("list = {}", list);
log.info("subList = {}", subList);
subList.add("E");
log.info("=== 修改子列表后");
log.info("list = {}", list);
log.info("subList = {}", subList);
list.add("K");
log.info("=== 修改原列表后");
log.info("list = {}", list);
log.info("subList = {}", subList); //TODO subList = [FAILED toString()] 异常java.util.ConcurrentModificationException
List<String> subList2 = new ArrayList<>(list.subList(1, 3));
subList2.add("F");
log.info("=== 截取子列表重新创建新list之后");
log.info("list = {}", list.size());
log.info("subList = {}", subList); //TODO subList = [FAILED toString()] 异常java.util.ConcurrentModificationException
log.info("subList2 = {}", subList2);
log.info("结论:sublist获取的是原列表的镜像;改子列表不会报错、但会影响原列表;改原列表会导致子列表报错,子列表依赖原列表");
}
执行结果:
应用(demo)
- 内存分页
public static void main(String[] args) {
//三个测试样本
List<String> list = new ArrayList<>(Arrays.asList("A", "B", "C", "D", "E", "F", "G", "H"));
//List<String> list = new ArrayList<>(Arrays.asList("A", "B", "C", "D", "E", "F", "G", "H", "I"));
//List<String> list = new ArrayList<>(Arrays.asList());
int groupSize = 3; //一组3个
int totalSize = list.size();
int handleTimes = totalSize == 0 ? 0 : (totalSize%groupSize == 0 ? (totalSize/groupSize) : (totalSize/groupSize + 1));//处理次数
for (int i = 1; i <= handleTimes; i++) {
List<String> subList = null;
if(i == handleTimes) {
subList = list.subList((i-1)*groupSize, totalSize);
} else {
subList = list.subList((i-1)*groupSize, i*groupSize);
}
log.info("分组 - {}", subList);
}
}
执行结果:
详细介绍
在使用 subList 方法时,需要注意以下几个关键点,以避免潜在的问题:
- 子列表与原列表共享数据
subList 返回的子列表不是一个新的列表,而是原列表的视图(view)。
对子列表的修改会影响原列表,反之亦然。
List list = new ArrayList<>(Arrays.asList(“A”, “B”, “C”, “D”));
List subList = list.subList(1, 3);
subList.set(0, “X”); // 修改子列表
System.out.println(list); // 输出: [A, X, C, D]
- 子列表不能独立于原列表操作
如果修改了原列表的结构(如增删元素),子列表会变得不可用,操作时会抛出 ConcurrentModificationException。
List list = new ArrayList<>(Arrays.asList(“A”, “B”, “C”, “D”));
List subList = list.subList(1, 3);
list.add(“E”); // 修改原列表结构
subList.get(0); // 抛出 ConcurrentModificationException
解决办法:
如果需要独立的子列表,建议创建一个新的列表:
List independentList = new ArrayList<>(list.subList(1, 3));
- 索引范围必须合法
subList(fromIndex, toIndex) 方法要求:
fromIndex >= 0
toIndex <= list.size()
fromIndex <= toIndex
如果索引越界,会抛出 IndexOutOfBoundsException。
List list = new ArrayList<>(Arrays.asList(“A”, “B”, “C”, “D”));
List subList = list.subList(2, 5); // 抛出 IndexOutOfBoundsException
- 子列表的并发问题
如果在多线程环境中使用 subList,要注意线程安全问题。
推荐在外部同步访问原列表和子列表,或使用线程安全的集合(如 Collections.synchronizedList)。 - 子列表的序列化问题
由于子列表依赖于原列表,因此在序列化时需要确保原列表也可序列化。否则,序列化子列表会失败。 - 使用场景
推荐:
用于对列表的一个范围执行只读操作。
用于分块操作(如分页处理)。
避免:
长期保存子列表引用,容易导致意外问题。
通过了解这些注意事项,可以更安全和高效地使用 subList 方法。