项目中使用Java中List.subList()的注意事项

使用介绍

在Java中,subListList接口的一个方法,用于获取原始列表的子列表

方法的声明如下

java 复制代码
List<E> subList(int fromIndex, int toIndex);
  • fromIndex:起始索引(包括)
  • toIndex:结束索引(不包括)
java 复制代码
List<Object> list = new Arraylist<>();

List<Object> subList = list.subList(0, 5);

返回的子列表是原始列表的一个视图,对子列表的修改会反映在原始列表上,反之亦然。

例如,下面的语句从列表中移除了元素的范围:

cs 复制代码
list.subList(from, to).clear();

下面是一个简单的例子:

java 复制代码
List<String> originalList = new ArrayList<>(Arrays.asList("A", "B", "C", "D", "E"));
List<String> subList = originalList.subList(1, 4);
System.out.println("subList = " + subList); // 子列表包括索引 1,2,3  输出:[B, C, D]
System.out.println("originalList = " + originalList); // 输出:[A, B, C, D, E]

subList.add("F");
System.out.println("subList = " + subList); // 输出: [B, C, D, F]
System.out.println("originalList = " + originalList);// 输出:[A, B, C, D, F, E] 因为通过subList视图对索引为4的位置添加了一个F


originalList.add("G");
// 调用subList()后,如果再对原list进行操作同时对subList()也进行操作(打印、添加、清除),都会报错ConcurrentModificationException,因为这会修改视图的结构
// subList.add("H") java.util.ConcurrentModificationException
// System.out.println(subList);  java.util.ConcurrentModificationException

System.out.println("originalList = " + originalList); // 输出:[A, B, C, D, F, E, G]
originalList.subList(1, 2).clear();
System.out.println("originalList = " + originalList); // 输出: [A, C, D, F, E, G] 因为通过视图对索引为1的数据进行了清除

注意事项

  • ArrayList的subList结果不可强转成ArrayList,否则会抛出ClassCastException 异常,即java.util.RandomAccessSubList cannot be cast to java.util.ArrayList.

说明:subList 返回的是 ArrayList 的内部类 SubList,并不是 ArrayList ,而是 ArrayList 的一个视图,对于 SubList 子列表的所有操作最终会反映到原列表上。

  • 在 subList 场景中,高度注意对原集合元素个数的修改,会导致子列表的遍历、增加、 删除均会产生 ConcurrentModificationException 异常。
  • 索引范围: fromIndextoIndex 是左闭右开的范围,即包括 fromIndex 处的元素,但不包括 toIndex 处的元素。
  • 修改原列表: 对子列表的修改会反映在原列表上,反之亦然。这是因为子列表实际上是原列表的一个视图。
  • 不支持结构性修改: 在子列表的视图上不支持对原列表的结构性修改(例如插入、删除元素),否则会抛出ConcurrentModificationException异常。结构性修改是指改变原列表的大小或者使其元素的数量发生变化的操作。
  • 子列表可以增、删、改,但是原列表不支持,理由同上
  • subList()返回的不是List !是**Sublist,**不是原来的类型
java 复制代码
 public List<E> subList(int fromIndex, int toIndex) {
        return (this instanceof RandomAccess ?
                new RandomAccessSubList<>(this, fromIndex, toIndex) :
                new SubList<>(this, fromIndex, toIndex));
    }
    声明:
    class SubList<E> extends AbstractList<E>{}
  • LinkedList并没有覆盖这个方法.ArryList自己覆盖了这个方法,返回的是java.util.ArrayList.SubList
java 复制代码
public List<E> subList(int fromIndex, int toIndex) {
    subListRangeCheck(fromIndex, toIndex, size);
    return new SubList(this, 0, fromIndex, toIndex);
}
声明:
private class SubList extends AbstractList<E> implements RandomAccess {}

看来ArryList处处体现出RandomAccess接口的特性------支持随机访问。

  • java.util.AbstractList.clear()方法就是subList的clear方法
java 复制代码
public void clear() {
    removeRange(0, size());
}

...
// ArrayList的覆盖
public void clear() {
    modCount++;
 
    // clear to let GC do its work
    for (int i = 0; i < size; i++)
        elementData[i] = null;
 
    size = 0;
}

...
// LinkedList的覆盖
public void clear() {
    for (Node<E> x = first; x != null; ) {
        Node<E> next = x.next;
        x.item = null;
        x.next = null;
        x.prev = null;
        x = next;
    }
    first = last = null;
    size = 0;
    modCount++;
}

...
// 根据上面,截短一个List的正确姿势
list.subList(from, to).clear();

项目实战

大家可以看一下我的慢查询优化实战文章

在最开始分页慢查询优化没有得到较好的解决方案时,我的操作是直接获取到全部数据的List,然后对List进行截取,达到分页的效果,这是相关的代码

java 复制代码
public static <T> IPage<T> listToPage(List<T> list, Integer current, Integer size){
        IPage<T> iPage = new Page<>(current,size);
        iPage.setTotal(list.size());
        int startIndex = (int)((current - 1)*size);
        if(null == list || list.isEmpty() || startIndex > list.size()){
            iPage.setRecords(new ArrayList<>());
        }
        else {
            int toIndex = (int)(current*size);
            iPage.setRecords(list.subList(startIndex,toIndex > list.size() ? list.size() : toIndex));
        }
        return iPage;
    }

大家可以看到,我对List是只截取不操作的,也就不会出现bug

注意!subList是返回一个镜像而不是新示例,用了得保证原来的list不能更改!

over

参考

Java中List的subList()方法及使用注意事项_list sublist-CSDN博客

相关推荐
lzb_kkk10 分钟前
【JavaEE】文件io
java·开发语言·java-ee·1024程序员节
yang_shengy11 分钟前
【JavaEE】多线程(1)
java·开发语言·jvm·java-ee
果壳~14 分钟前
【Java】SpringBoot模拟流式输出,前端使用流式接收数据并打印
java·前端·spring boot
优雅的小武先生14 分钟前
【Qt】报错error: undefined reference to `vtable for的最简单解决
开发语言·qt
only-lucky15 分钟前
QT之QML从入门到精通(第七章)
java·数据库·qt
郑同学的笔记15 分钟前
【Qt教程03】Qt中的信号和槽
开发语言·c++·qt
Bruce小鬼19 分钟前
QT基本绘图
开发语言·qt·命令模式
带刺的坐椅20 分钟前
Solon MVC 的 @Mapping 用法说明
java·mvc·ioc·solon
飞滕人生TYF22 分钟前
java Arrays 详解
java·python·排序算法
xisai8825 分钟前
2025年开考科目有哪些?
java·开发语言·javascript·算法·kotlin