本文是本人的学习笔记,把本身的理解总结记录下来。因本人水平有限,若是您在阅读中发现错误,还望谅解,而且但愿可以告知本人改正,不胜感激!java
ArrayList中的subList(int fromIndex, int toIndex)方法,是返回当前ArrayList中从索引为fromIndex的位置索引位置为toIndex-1的位置的列表 视图。这里须要注意几点:数组
subList方法传入的参数是左闭右开的,也就是[formIndex,toIndex)的形式。因此若是传来两个相同的index进去会返回一个空的列表。app
public class SubListTest { public static void main(String[] args) { List<String> list = new ArrayList<String>(); list.add("1"); list.add("2"); list.add("3"); list.add("4"); List<String> subList = list.subList(1, 1); System.out.println("subList: " + subList); // subList: [] } }
subList方法返回的只是一个视图,是一个对源列表的映射。这意味着若是对subList返回的列表进行编辑操做,源列表也会收到影响。函数
对subList方法返回的列表进行添加、删除元素会抛出异常。学习
public class SubListTest { public static void main(String[] args) { List<String>; list = new ArrayList<String>(); list.add("1"); list.add("2") list.add("3"); list.add("4"); List<String> subList = list.subList(0, 2); System.out.println(subList); list.remove(1); System.out.println("subList:" + subList); System.out.println("list:" + list); } }
抛出异常:ui
Exception in thread "main" java.util.ConcurrentModificationException at java.util.ArrayList$SubList.checkForComodification(ArrayList.java:1239) at java.util.ArrayList$SubList.listIterator(ArrayList.java:1099) at java.util.AbstractList.listIterator(AbstractList.java:299) at java.util.ArrayList$SubList.iterator(ArrayList.java:1095) at java.util.AbstractCollection.toString(AbstractCollection.java:454) at java.lang.String.valueOf(String.java:2994) at java.lang.StringBuilder.append(StringBuilder.java:131) at com.lijunpeng.List.SubListTest.main(SubListTest.java:23)
这是由于触发fail-fast机制,致使抛出异常,关于fail-fase机制,能够参见这篇博客:Java中的fail-fast机制this
public List<E> subList(int fromIndex, int toIndex) { // 判断参数的合法性 subListRangeCheck(fromIndex, toIndex, size); // 返回一个SubList实例 return new SubList(this, 0, fromIndex, toIndex); }
该方法会返回一个SubList的实例,SubList是一个ArrayList的内部类,其构造方法以下:code
// SubList中的属性 private final AbstractList<E> parent; private final int parentOffset; private final int offset; int size; // 构造方法 SubList(AbstractList<E> parent, int offset, int fromIndex, int toIndex) { this.parent = parent; this.parentOffset = fromIndex; this.offset = offset + fromIndex; this.size = toIndex - fromIndex; // 这个参数就是触发fail-fast机制的关键 this.modCount = ArrayList.this.modCount; }
能够看到,SubList中并无数组、列表之类的属性来存储数据,这进一步说明了,subList方法返回的只是一个视图。 以前说过,对subList方法返回的列表进行修改,源列表也会跟着发生变化,下面是SubList的set方法orm
public E set(int index, E e) { // 传入的参数索引合法性校验 rangeCheck(index); // 这个方法就是用来检验modCount从而触发fail-fast机制, // 可是set这里modCount不会发生变化,因此不会触发fial-fast机制 checkForComodification(); // 从外部类(源列表)获取值 E oldValue = ArrayList.this.elementData(offset + index); // 讲外部类(源列表)的值替换成新的值,ArrayList的值是存在elementData的数组中的。 ArrayList.this.elementData[offset + index] = e; return oldValue; }
从上面代码看出,修改subList的值实际上是在修改源列表的值的,因此源列表值发生变化是必然的。 接下来再看一下为何在源列表中删除或添加会抛出异常呢,首先咱们先看一下以前报错的信息:索引
Exception in thread "main" java.util.ConcurrentModificationException at java.util.ArrayList$SubList.checkForComodification(ArrayList.java:1239) at java.util.ArrayList$SubList.listIterator(ArrayList.java:1099) at java.util.AbstractList.listIterator(AbstractList.java:299) at java.util.ArrayList$SubList.iterator(ArrayList.java:1095) at java.util.AbstractCollection.toString(AbstractCollection.java:454) at java.lang.String.valueOf(String.java:2994) at java.lang.StringBuilder.append(StringBuilder.java:131) at com.lijunpeng.List.SubListTest.main(SubListTest.java:23)
首先报错的是main函数的23也就是:System.out.println("subList:" + subList) 这一句。这一步并无操做subList,是调用了其toString方法,进一步调用了SubList的迭代器方法。因此须要往下看错误信息,发如今checkForComodification这个方法中报错,上面在set方法中也有这个方法,而且这个方法就是触发java的fail-fast机制。因此看一下这个checkForComodification方法的代码:
private void checkForComodification() { if (ArrayList.this.modCount != this.modCount) throw new ConcurrentModificationException(); }
checkForComodificatio中比较了SubList类中的modCount和外部类ArrayList中的modCount是否相等,不相等就会抛出ConcurrentModificationException,触发fail-fast机制。那为何这两个modeCount不相等呢?首先须要再回顾一下SubList类中的构造函数:
// SubList的构造函数 SubList(AbstractList parent, int offset, int fromIndex, int toIndex) { this.parent = parent; this.parentOffset = fromIndex; this.offset = offset + fromIndex; this.size = toIndex - fromIndex; // 在构造时,SubList中的modCount和ArrayList中的modCount是一致的 this.modCount = ArrayList.this.modCount; }
接着,看一下ArrayList中的add方法,注意:代码中调用的是list.add而不是subList.add,因此须要看ArrayList的add源码:
public boolean add(E e) { ensureCapacityInternal(size + 1); elementData\[size++\] = e; return true; } private void ensureCapacityInternal(int minCapacity) { ensureExplicitCapacity(calculateCapacity(elementData, minCapacity)); } private void ensureExplicitCapacity(int minCapacity) { // 这里ArrayList的modCount发生了变化! modCount++; if (minCapacity - elementData.length > 0) grow(minCapacity); }
示例代码中:在list.add("5"),以后就直接打印了,也就是说咱们没有再对SubList进行操做,SubList中的modCount也没有发生改变,全部 ArrayList.this.modCount != this.modCount 成立,致使抛出了ConcurrentModificationException异常。这里不只打印会抛出异常,只要对subList进行操做都会抛出,由于SubList的方法都进行了checkForComodificatio,检查这两个modCount。
可是若是只对subList方法返回的列表:subList进行增删元素的操做,并不会抛出异常,可是一样会影响源列表,由于subList的全部操做都是在操做源列表:
public class SubListTest { public static void main(String[] args) { List<String> list = new ArrayList<String>(); list.add("1"); list.add("2"); list.add("3"); list.add("4"); List<String> subList = list.subList(0, 2); System.out.println(subList); subList.add("3"); System.out.println("subList:" + subList); // subList:[1, 2, 3] System.out.println("list:" + list); // list:[1, 2, 3, 3, 4] }}
public class SubListTest { public static void main(String[] args) { List<String> list = new ArrayList<String>(); list.add("1"); list.add("2"); list.add("3"); list.add("4"); List<String> subList = list.subList(0, 2); List<String> newList = new ArrayList<String>(); newList.addAll(subList); newList.set(0, "new1"); System.out.println("list:" + list); // list:[1, 2, 3, 4] System.out.println("subList:" + subList); // subList:[1, 2] System.out.println("newList:" + newList); // newList:[new1, 2] } }