首先sublist(int start,int end)是前闭后开,之前用list.sublist()时候总觉得是截取一部分形成一个新的集合,有次在工作中发现已经截取过的新的list在其下方代码执行之后报错:java.util.ConcurrentModificationException
才发现sublist()其实不是截取,而是映射,或者说用数据库的视图原理更加贴切点,sublist返回的事原数组的start和end的引用地址,而不是真正的list实体集合:
下图就是模拟踩坑的代码:
list1并未进行任何操作,当执行第二个打印list1的时候就报错了;
这里,我将尝试解读 subList()
方法的源码实现,重点在于理解这个视图的机制。请注意,由于 subList()
是 List
接口的一部分,其具体的实现可能会根据具体的 List
实现类(如 ArrayList
, LinkedList
等)而有所不同。这里,我将以 ArrayList
的实现为例来解读。
首先,这是 ArrayList
中 subList()
方法的大致实现(具体的实现可能因 Java 版本而异):
public List<E> subList(int fromIndex, int toIndex) {
subListRangeCheck(fromIndex, toIndex, size);//检查入参规范性
return new SubList(this, fromIndex, toIndex); //生成视图返回
}
这个方法首先检查 fromIndex
和 toIndex
的范围是否有效,然后返回一个新的 SubList
对象。
SubList
是 ArrayList
的一个内部类,它实现了 RandomAccess
接口(这是 List
接口的一个标记接口,表示支持快速随机访问),并继承了 AbstractList
类。SubList
的关键部分在于其实现了 List
接口中的方法,如 get()
, set()
, add()
, remove()
等,这些方法都会将操作映射到原 ArrayList
的相应部分。
例如,get()
方法的实现如下:
public E get(int index) {
rangeCheck(index);
return list.get(this.offset + index);
}
这里,this.offset
是 SubList
在原 ArrayList
中的起始位置。当你从 SubList
中获取一个元素时,实际上是从原 ArrayList
中获取对应位置的元素。
同样,对于 set()
, add()
, remove()
等修改操作,SubList
也会将操作映射到原 ArrayList
的相应部分,并相应地更新其内部的状态。
通过这种方式,subList()
方法创建了一个原列表的视图,而不是一个全新的、独立的列表。这个视图与原列表共享数据,因此对视图的任何修改都会反映到原列表中,反之亦然。这就是 subList()
方法视图机制的核心。
需要注意的是,由于 subList()
返回的是一个视图,而不是一个独立的列表,因此在某些情况下需要小心使用。例如,如果你在迭代 subList
的同时修改了原列表(或 subList
本身),那么迭代可能会抛出 ConcurrentModificationException
异常。因此,在使用 subList()
时,最好确保在迭代期间不要修改原列表或 subList
。或者就new list(原表的sublist视图) 这样收到的就是独立存在的片段了;同样,如果对sublist生成的对象进行操作也会影响parent集合