本文研究List集合的subList方法,测试方式为:新建一个集合,然后截取原集合的部分元素,然后去操作新集合和原集合来观察结果。
1.新集合中添加元素
public static void testSubList(){
List list = new ArrayList<>();
list.add(1);
list.add(2);
list.add(3);
list.add(4);
list.add(5);
list.add(5);
list.add(6);
//截取集合
List subList = list.subList(0, 3);
System.out.println(subList);
//截取的新集合添加元素
subList.add(33);
System.out.println(subList);
System.out.println(list);
}
结果为:
[1, 2, 3]
[1, 2, 3, 33]
[1, 2, 3, 33, 4, 5, 5, 6]
结论:我们会发现,虽然list 和 subList是两个不同的对象,但是我们在操作新集合时,发现原集合的数据也改变了,看一下源码:
SubList(AbstractList<E> parent,
int offset, int fromIndex, int toIndex) {
this.parent = parent;
this.parentOffset = fromIndex;
this.offset = offset + fromIndex;
this.size = toIndex - fromIndex;
this.modCount = ArrayList.this.modCount;
}
这里的parent是原集合,sunList返回的实际上是原集合的部分视图,所以你去修改新集合时,实际是把原集合改了。
2.原始集合添加元素
public static void testSubList2(){
List list = new ArrayList<>();
list.add(1);
list.add(2);
list.add(3);
list.add(4);
list.add(5);
list.add(5);
list.add(6);
//截取集合
List subList = list.subList(0, 3);
System.out.println(subList);
System.out.println(list);
//原始集合添加元素
list.add(1000);
System.out.println(list);
//System.out.println(subList);
}
结果:
[1, 2, 3]
[1, 2, 3, 4, 5, 5, 6]
[1, 2, 3, 4, 5, 5, 6, 1000]
如果再去打印subList,会报错:
java.util.ConcurrentModificationException
3.报错分析
这个打印输出为什么会报错呢?因为在打印时,其实是在迭代元素然后拼接后打印输出,在迭代的时候,出现了这个错误,我们按照调用链详细的追溯一下源码:
打印对象
public void println(Object x) {
String s = String.valueOf(x);
synchronized (this) {
print(s);
newLine();
}
}
对象要toString()成字符串
public static String valueOf(Object obj) {
return (obj == null) ? "null" : obj.toString();
}
打印的是集合,那转字符串时要迭代出元素才行,得有个迭代器
public String toString() {
Iterator<E> it = iterator();
if (! it.hasNext())
return "[]";
StringBuilder sb = new StringBuilder();
sb.append('[');
for (;;) {
E e = it.next();
sb.append(e == this ? "(this Collection)" : e);
if (! it.hasNext())
return sb.append(']').toString();
sb.append(',').append(' ');
}
}
获取迭代器
public Iterator<E> iterator() {
return listIterator();
}
获取List集合的迭代器
public ListIterator<E> listIterator() {
return listIterator(0);
}
迭代器内部
扫描二维码关注公众号,回复:
1527227 查看本文章
public ListIterator<E> listIterator(final int index) {
checkForComodification();
rangeCheckForAdd(index);
final int offset = this.offset;
return new ListIterator<E>() {
int cursor = index;
int lastRet = -1;
int expectedModCount = ArrayList.this.modCount;
public boolean hasNext() {
return cursor != SubList.this.size;
}
@SuppressWarnings("unchecked")
public E next() {
checkForComodification();
int i = cursor;
if (i >= SubList.this.size)
throw new NoSuchElementException();
Object[] elementData = ArrayList.this.elementData;
if (offset + i >= elementData.length)
throw new ConcurrentModificationException();
cursor = i + 1;
return (E) elementData[offset + (lastRet = i)];
}
。。。。。。
}
迭代前要做个检查,看看集合有没有被改变,这两个值如果不相等,就抛出错误,这就是我们之前打印抛出的那个错误。
private void checkForComodification() {
if (ArrayList.this.modCount != this.modCount)
throw new ConcurrentModificationException();
}
由于在截取新集合时,原来集合的的size是7,那么在你subList截取的新集合中,这个modCount也是7,现在,我们在原集合中添加了元素,那原来集合的modCount已经成8了,而这个截取获得的集合,modCount是7,比较后不相等,所以抛出了错误。