java.util.ArrayListjava
ArrayList繼承自AbstractList,AbstractList爲隨機訪問數據的結構,如數組提供了基本實現,而且提供了Iterator。首先看AbstractList實現了什麼方法。數組
null能夠做爲一項存儲在ArrayList中。ListIterator是遍歷訪問AbstractList中元素較好的方式,須要注意獲取元素序號的方法是previousIndex,而不是nextIndex,由於以前有next方法的調用。還有,這裏判斷兩個元素是否相等採用的是equals方法,而不是==
。app
public int indexOf(Object o) { ListIterator<E> it = listIterator(); if (o==null) { while (it.hasNext()) if (it.next()==null) return it.previousIndex(); } else { while (it.hasNext()) if (o.equals(it.next())) return it.previousIndex(); } return -1; }
從後向前遍歷AbstractList,過程偏偏相反。less
public int lastIndexOf(Object o) { ListIterator<E> it = listIterator(size()); if (o==null) { while (it.hasPrevious()) if (it.previous()==null) return it.nextIndex(); } else { while (it.hasPrevious()) if (o.equals(it.previous())) return it.nextIndex(); } return -1; }
若是在使用Iterator時,有其餘線程嘗試去修改List的大小,會被發現且當即拋出異常。ide
能夠看class Itr implements Iterator<E>
中,有屬性int expectedModCount = modCount;
記錄着指望的數組大小,若是不一致,會拋出ConcurrentModificationException
。函數
有兩個遊標分別記錄當前指向的位置和上一次指向的位置。this
/** * Index of element to be returned by subsequent call to next. */ int cursor = 0; /** * Index of element returned by most recent call to next or * previous. Reset to -1 if this element is deleted by a call * to remove. */ int lastRet = -1;
Iterator能夠正確的刪除AbstractList中的元素,而且保證訪問的順序的正確性。線程
public void remove() { if (lastRet < 0) throw new IllegalStateException(); checkForComodification(); try { AbstractList.this.remove(lastRet); if (lastRet < cursor) cursor--; lastRet = -1; expectedModCount = modCount; } catch (IndexOutOfBoundsException e) { throw new ConcurrentModificationException(); } }
刪除的元素是上一個next調用後的元素,而且連續調用兩次remove會拋出異常,由於元素只能刪除一次,上次指向的元素已經沒有了。code
在class ListItr extends Itr implements ListIterator<E>
中實現了向list添加元素的方法。添加的位置是上一次next返回的元素以後,下一個next以前。對象
public void add(E e) { checkForComodification(); try { int i = cursor; AbstractList.this.add(i, e); lastRet = -1; cursor = i + 1; expectedModCount = modCount; } catch (IndexOutOfBoundsException ex) { throw new ConcurrentModificationException(); } }
就是用了一個簡單的數組。。。
/** * The array buffer into which the elements of the ArrayList are stored. * The capacity of the ArrayList is the length of this array buffer. Any * empty ArrayList with elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA * will be expanded to DEFAULT_CAPACITY when the first element is added. */ transient Object[] elementData; // non-private to simplify nested class access
使用trimToSize函數,將原始數組copy到適合大小的數組中。
/** * Trims the capacity of this <tt>ArrayList</tt> instance to be the * list's current size. An application can use this operation to minimize * the storage of an <tt>ArrayList</tt> instance. */ public void trimToSize() { modCount++; if (size < elementData.length) { elementData = (size == 0) ? EMPTY_ELEMENTDATA : Arrays.copyOf(elementData, size); } }
不能否認,Iterator爲遍歷一個集合提供了統一的接口,用戶能夠忽略集合內部的具體實現,可是過多的封裝會致使效率的下降。顯然,Java開發人員認爲經過下標遍歷ArrayList的數組結構更加高效。因此重寫了indexOf和lastIndexOf。
public int indexOf(Object o) { if (o == null) { for (int i = 0; i < size; i++) if (elementData[i]==null) return i; } else { for (int i = 0; i < size; i++) if (o.equals(elementData[i])) return i; } return -1; }
ArrayList只是新建了數組,copy了元素的引用,元素自己沒有進行copy。clone方法爲何不new一個ArrayList對象,而是調用了Object類的clone?由於Object的clone函數是native的,更高效。
/** * Returns a shallow copy of this <tt>ArrayList</tt> instance. (The * elements themselves are not copied.) * * @return a clone of this <tt>ArrayList</tt> instance */ public Object clone() { try { //使用Object的clone得到一個對象 ArrayList<?> v = (ArrayList<?>) super.clone(); v.elementData = Arrays.copyOf(elementData, size); v.modCount = 0; return v; } catch (CloneNotSupportedException e) { // this shouldn't happen, since we are Cloneable throw new InternalError(e); } }
若是list初始沒有元素,使用一個靜態的空數組。元素增長時,空間擴展爲默認的10。在後續的擴展過程當中,容量會每次增長原始大小的1/2。
雖然AbstractList實現了iterator,但ArrayList彷佛不太滿意,又從新實現了一遍。主要區別就是在獲取元素時,利用了數組結構的優點,能夠直接經過下標獲取元素,而沒必要經過調用方法。
ArrayList理所固然的實現了本身的Spliterator,也就是ArrayListSpliterator
。分割的策略簡而言之爲:二分+延遲初始化。
ArrayListSpliterator有以下屬性:
this.list = list; // OK if null unless traversed 保存目標list this.index = origin; //起始位置 this.fence = fence; //終止位置 this.expectedModCount = expectedModCount; //指望修改次數,用來判斷運行時是否有其它線程修改
每次從中間開始分裂。在進行分裂時,原始spliterator保留中部至末尾的元素,新的spliterator保留原起始位置到中部的元素。
public ArrayListSpliterator<E> trySplit() { int hi = getFence(), lo = index, mid = (lo + hi) >>> 1; return (lo >= mid) ? null : // divide range in half unless too small new ArrayListSpliterator<E>(list, lo, index = mid, expectedModCount); }
在第一次建立spliterator時,fence被初始爲-1,因此在實際使用spliterator時,getFence纔會被調用,從而fence纔會被賦值爲數組大小。一樣,expectedModCount也會進行從新賦值,使得spliterator在使用前與list保持一致。
private int getFence() { // initialize fence to size on first use int hi; // (a specialized variant appears in method forEach) ArrayList<E> lst; if ((hi = fence) < 0) { if ((lst = list) == null) hi = fence = 0; else { expectedModCount = lst.modCount; hi = fence = lst.size; } } return hi; }
在遍歷list時,會調用方法tryAdvance
,將動做施加於元素之上。該方法會檢查是否有其它線程的修改,在ArrayList中,有大量方法都會使用modCount
來記錄修改,或者判斷是否在方法執行時有其它線程的修改,目前看來,用這個方法來檢測是否有並行修改使得list結構變化是有效的,能夠避免並行修改帶來的一些問題。
public boolean tryAdvance(Consumer<? super E> action) { if (action == null) throw new NullPointerException(); int hi = getFence(), i = index; if (i < hi) { index = i + 1; @SuppressWarnings("unchecked") E e = (E)list.elementData[i]; action.accept(e); if (list.modCount != expectedModCount) throw new ConcurrentModificationException(); return true; } return false; }