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

YuuuZh。 2024-07-20 15:05:02 阅读 95

使用介绍

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

方法的声明如下

<code>List<E> subList(int fromIndex, int toIndex);

fromIndex:起始索引(包括)toIndex:结束索引(不包括)

List<Object> list = new Arraylist<>();

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

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

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

list.subList(from, to).clear();

下面是一个简单的例子:

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,不是原来的类型

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

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 {}

wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==

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

java.util.AbstractList.clear()方法就是subList的clear方法

<code>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进行截取,达到分页的效果,这是相关的代码

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博客



声明

本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。