ArrayList繼承自AbstractList,AbstractList爲「random access」的數組提供了基本的實現。隨機訪問數據是相對於順序訪問數據而言,例如鏈表的形式。AbstractSequentialList爲鏈表形式的順序訪問提供了基本實現。AbstractList提供了默認的隨機訪問數據的iterator。AbstractList繼承自AbstractCollection,AbstractCollection爲Collection提供了基本實現。java
java.util.AbstractCollection數組
<!--more-->安全
AbstractCollection實現了查詢是否包含某一個元素的方法。最好使用Iterator遍歷集合中的元素,由於能夠屏蔽集合內部元素存儲的具體實現,而且根據不一樣的數據存儲特色,優化訪問策略。這裏還能夠正確查找null元素,須要注意的是對null元素的查詢須要特別的處理,有時候本身實現方法時,每每會忽略傳入參數爲null時的處理,致使方法沒法處理特殊狀況。併發
public boolean contains(Object o) { Iterator<E> it = iterator(); if (o==null) { while (it.hasNext()) if (it.next()==null) return true; } else { while (it.hasNext()) if (o.equals(it.next())) return true; } return false; }
toArray
方法將容器中的元素轉化爲數組的形式。這裏元素的複製,採用的是直接複製引用。這裏還考慮到了併發運算時,元素數量在複製時產生變化的狀況,當數量減小時,就用Arrays.copy()
截取結果。當數量增長時,會調用finishToArray
函數擴容。app
public Object[] toArray() { // Estimate size of array; be prepared to see more or fewer elements Object[] r = new Object[size()]; Iterator<E> it = iterator(); for (int i = 0; i < r.length; i++) { if (! it.hasNext()) // fewer elements than expected return Arrays.copyOf(r, i); r[i] = it.next(); } return it.hasNext() ? finishToArray(r, it) : r; }
在finishToArray函數中,對容量每次擴展1/2+1的大小,而且會檢查是否會超過設定的最大數組長度MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8
。若是長度超過了限定值,則以原容量+1爲底線,返回一個最大容量。最後返回的數組也會修剪掉多餘的位置。dom
private static <T> T[] finishToArray(T[] r, Iterator<?> it) { int i = r.length; while (it.hasNext()) { int cap = r.length; if (i == cap) { int newCap = cap + (cap >> 1) + 1; // overflow-conscious code if (newCap - MAX_ARRAY_SIZE > 0) newCap = hugeCapacity(cap + 1); r = Arrays.copyOf(r, newCap); } r[i++] = (T)it.next(); } // trim if overallocated return (i == r.length) ? r : Arrays.copyOf(r, i); } private static int hugeCapacity(int minCapacity) { if (minCapacity < 0) // overflow throw new OutOfMemoryError ("Required array size too large"); return (minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE : MAX_ARRAY_SIZE; }
這裏有一個toArray
的重載,須要傳入一個數組做爲參數,據我觀察,目的是將集合中的元素存入指定的數組,重利用已有數組的存儲。若是元素過多,而目標數組容納不下,只能從新申請數組來進行存儲。函數
public <T> T[] toArray(T[] a) { // Estimate size of array; be prepared to see more or fewer elements int size = size(); T[] r = a.length >= size ? a : (T[])java.lang.reflect.Array .newInstance(a.getClass().getComponentType(), size); Iterator<E> it = iterator(); for (int i = 0; i < r.length; i++) { if (! it.hasNext()) { // fewer elements than expected if (a == r) { //利用的原數組,且元素不足,則補null表示結束 r[i] = null; // null-terminate } else if (a.length < i) { //元素較少,但數量超過原始數組,只能用新數組 return Arrays.copyOf(r, i); } else { //元素減小,致使原始數組能夠容納下,則copy回原始數組,折騰唄 System.arraycopy(r, 0, a, 0, i); if (a.length > i) { a[i] = null; } } return a; } r[i] = (T)it.next(); } // more elements than expected return it.hasNext() ? finishToArray(r, it) : r; }
removeAll
方法刪除目標集合中的全部重疊元素。Iterator能夠作到在遍歷時,安全的刪除元素,而經過index循環刪除則可能致使index偏移。優化
public boolean removeAll(Collection<?> c) { Objects.requireNonNull(c); boolean modified = false; Iterator<?> it = iterator(); while (it.hasNext()) { if (c.contains(it.next())) { it.remove(); modified = true; } } return modified; }
AbstractCollection的toString實現,比較令我震驚的是e == this
的做用。這裏爲何要判斷打印的內容是否爲本身自己?由於sb.append(e == this ? "(this Collection)" : e);
會調用e
的toString函數來生成字符串,若不加判斷,則會造成無限的toString遞歸調用。我猜測一下,估計當時toString的實現者沒有注意到該問題的存在,直到該bug出現,腦洞不大還真難想起來這個問題。ui
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(' '); } }