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返回的实际上是原集合的部分视图,所以你去修改新集合时,实际是把原集合改了。
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
这个打印输出为什么会报错呢?因为在打印时,其实是在迭代元素然后拼接后打印输出,在迭代的时候,出现了这个错误,我们按照调用链详细的追溯一下源码:
打印对象
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);
}
迭代器内部
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,比较后不相等,所以抛出了错误。
看一下源码中对此方法的解释:
/**
* Returns a view of the portion of this list between the specified
* <tt>fromIndex</tt>, inclusive, and <tt>toIndex</tt>, exclusive. (If
* <tt>fromIndex</tt> and <tt>toIndex</tt> are equal, the returned list is
* empty.) The returned list is backed by this list, so non-structural
* changes in the returned list are reflected in this list, and vice-versa.
* The returned list supports all of the optional list operations supported
* by this list.<p>
*
* This method eliminates the need for explicit range operations (of
* the sort that commonly exist for arrays). Any operation that expects
* a list can be used as a range operation by passing a subList view
* instead of a whole list. For example, the following idiom
* removes a range of elements from a list:
* <pre>{@code
* list.subList(from, to).clear();
* }</pre>
* Similar idioms may be constructed for <tt>indexOf</tt> and
* <tt>lastIndexOf</tt>, and all of the algorithms in the
* <tt>Collections</tt> class can be applied to a subList.<p>
*
* The semantics of the list returned by this method become undefined if
* the backing list (i.e., this list) is <i>structurally modified</i> in
* any way other than via the returned list. (Structural modifications are
* those that change the size of this list, or otherwise perturb it in such
* a fashion that iterations in progress may yield incorrect results.)
*
* @param fromIndex low endpoint (inclusive) of the subList
* @param toIndex high endpoint (exclusive) of the subList
* @return a view of the specified range within this list
* @throws IndexOutOfBoundsException for an illegal endpoint index value
* (<tt>fromIndex < 0 || toIndex > size ||
* fromIndex > toIndex</tt>)
*/
List<E> subList(int fromIndex, int toIndex);
我们翻译一下这个方法:
返回这个列表在指定的[fromIndex,toIndex)之间的视图。
如果fromIndex=toIndex,那么返回的列表为空。
返回的列表由这个列表支持,所以在返回列表中非结构性的改变会反映在这个列表中,反之亦然。
返回的集合支持这个集合所支持的所有的可选的集合操作。(这个怎么翻译都感觉怪怪的,但是这样翻译应该是可以理解意思的)
这个方法不需要显示的范围操作(数组中常见的操作),任何希望可以用来范围操作的一个集合可以通过一个截取视图来代替完整的集合。
下面的例句,从一个集合中移除一个范围的元素。
list.subList(from, to).clear();
相似的语句可以用于indexOf,lastIndexOf,以及Collections集合中的所有算法都可以应用于subList.(这个,还请高人指点)
如果这个集合以任何方式发生了结构性修改,这个方法返回的集合将会变成未定义的,除了通过这个返回的集合。