使用介绍
在Java中,subList是List接口的一个方法,用于获取原始列表的子列表
方法的声明如下
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的位置添加了一个ForiginalList.add("G");
// 调用subList()后,如果再对原list进行操作同时对subList()也进行操作(打印、添加、清除),都会报错ConcurrentModificationException,因为这会修改视图的结构
// subList.add("H") java.util.ConcurrentModificationException
// System.out.println(subList); java.util.ConcurrentModificationExceptionSystem.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 异常。
- 索引范围:
fromIndex
至toIndex
是左闭右开的范围,即包括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 {}
看来ArryList处处体现出RandomAccess接口的特性——支持随机访问。
- java.util.AbstractList.clear()方法就是subList的clear方法
public void clear() {removeRange(0, size());
}...
// ArrayList的覆盖
public void clear() {modCount++;// clear to let GC do its workfor (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博客