本文是本人的學習筆記,把本身的理解總結記錄下來。因本人水平有限,若是您在閱讀中發現錯誤,還望諒解,而且但願可以告知本人改正,不勝感激!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] } }