sublist使用和踩坑

发布于:2024-12-18 ⋅ 阅读:(100) ⋅ 点赞:(0)

内存分页会用到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 方法时,需要注意以下几个关键点,以避免潜在的问题:

  1. 子列表与原列表共享数据
    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]

  1. 子列表不能独立于原列表操作
    如果修改了原列表的结构(如增删元素),子列表会变得不可用,操作时会抛出 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));

  1. 索引范围必须合法
    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

  1. 子列表的并发问题
    如果在多线程环境中使用 subList,要注意线程安全问题。
    推荐在外部同步访问原列表和子列表,或使用线程安全的集合(如 Collections.synchronizedList)。
  2. 子列表的序列化问题
    由于子列表依赖于原列表,因此在序列化时需要确保原列表也可序列化。否则,序列化子列表会失败。
  3. 使用场景
    推荐:

用于对列表的一个范围执行只读操作。
用于分块操作(如分页处理)。
避免:

长期保存子列表引用,容易导致意外问题。
通过了解这些注意事项,可以更安全和高效地使用 subList 方法。


网站公告

今日签到

点亮在社区的每一天
去签到