.html
.java
.數組
.數據結構
.框架
目錄函數
(一)一塊兒學 Java Collections Framework 源碼之 概述測試
(二)一塊兒學 Java Collections Framework 源碼之 AbstractCollectionui
java.util.AbstractCollection 類提供了 java.util.Collection 接口的骨幹實現,也是 Java 集合框架(JCF, Java Collections Framework)中列表(List/Set)族相對較爲頂層的實現類,這一點你們經過上一篇博文的 圖1 就能夠看出來了。此類是抽象類,所以僅僅實現了 Collection 接口中一些最基本的方法。因爲此類沒有實現 List 接口,因此它的子類未必都是有序的,所以它能夠做爲 List(有序)實現類和 Set(無序)實現類的共同祖先類。spa
JCF 爲容器的基本實現提供了多個 Abstract 類,所以掌握了這些類以後,咱們本身基於這些 Abstract 類再實現新的容器時只要實現那些必要的方法便可。指針
下面咱們對 AbstractCollection 類中一些常見的方法實現逐個進行分析。
1.iterator() 方法
public abstract Iterator<E> iterator();
此方法返回一個迭代器,該迭代器用於遍歷這個列表中的每個元素。因爲 java.util.Iterator 也是一個接口,因此不一樣的數據結構實現(也就是不一樣的子類)能夠根據本身的須要來定義不一樣的迭代方式,因此此方法被定義爲一個抽象方法,沒有對它進行實現。
2.isEmpty() 方法
1 public boolean isEmpty() { 2 return size() == 0; 3 }
這個很好理解,用來判斷當前集合是否是空的,不作過多的解釋。
3.contains(Object o) 方法
1 public boolean contains(Object o) { 2 Iterator<E> it = iterator(); 3 if (o==null) { 4 while (it.hasNext()) 5 if (it.next()==null) 6 return true; 7 } else { 8 while (it.hasNext()) 9 if (o.equals(it.next())) 10 return true; 11 } 12 return false; 13 }
此方法用來判斷當前集合中是否包含一個與傳入的參數 o 相同的元素。
想要匹配某個元素就必需要遍歷集合了,因而先得到迭代器,而後根據參數 o 是否爲 null 分別進行不一樣的處理。
這裏須要注意的是,在參數 o 不爲 null 時採用的是 equals 方法來對比的。若是咱們要使用特定的規則來判斷這個對象是否存在於某個集合中,經過重寫該對象的 equals() 方法便可實現。
4.toArray() 方法
1 public Object[] toArray() { 2 // Estimate size of array; be prepared to see more or fewer elements 3 Object[] r = new Object[size()]; 4 Iterator<E> it = iterator(); 5 for (int i = 0; i < r.length; i++) { 6 if (! it.hasNext()) // fewer elements than expected 7 return Arrays.copyOf(r, i); 8 r[i] = it.next(); 9 } 10 return it.hasNext() ? finishToArray(r, it) : r; 11 } 12 13 private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8; 14 15 @SuppressWarnings("unchecked") 16 private static <T> T[] finishToArray(T[] r, Iterator<?> it) { 17 int i = r.length; 18 while (it.hasNext()) { 19 int cap = r.length; 20 if (i == cap) { 21 int newCap = cap + (cap >> 1) + 1; 22 // overflow-conscious code 23 if (newCap - MAX_ARRAY_SIZE > 0) 24 newCap = hugeCapacity(cap + 1); 25 r = Arrays.copyOf(r, newCap); 26 } 27 r[i++] = (T)it.next(); 28 } 29 // trim if overallocated 30 return (i == r.length) ? r : Arrays.copyOf(r, i); 31 } 32 33 private static int hugeCapacity(int minCapacity) { 34 if (minCapacity < 0) // overflow 35 throw new OutOfMemoryError 36 ("Required array size too large"); 37 return (minCapacity > MAX_ARRAY_SIZE) ? 38 Integer.MAX_VALUE : 39 MAX_ARRAY_SIZE; 40 }
toArray() 方法調用了兩個私有方法,雖然看起來比較長,但解讀起來並不複雜。
這個方法用於將集合中全部元素轉換爲數組。
首先建立一個與集合長度相同的數組 r,並經過迭代器將集合中的每個元素按照迭代的順序依次保存到數組 r 中。
但因爲獲取集合長度的方法 size() 是由子類來實現的,因此沒法避免兩種狀況發生:
1) size() 返回的長度比真正集合中元素的數量多;
2) size() 返回的長度比真正集合中元素的數量少。
數組 r 的長度又是固定的,怎麼辦?
第一種狀況,經過 java.util.Arrays.copyOf(Object[] original, int newLength) 方法根據真正的元素數量再建立一個新的數組,而後將 r 內現有的元素拷貝進去再返回便可。
第二種狀況稍微麻煩一點,由於此時並不知道集合中還有多少個元素沒有被迭代出來,所以是不能直接肯定新分配的數組的長度的。
這時候兩個私有函數就派上用場了:finishToArray(T[] r, Iterator<?> it) 方法會繼續迭代這個集合,在遍歷的過程當中會經過 java.util.Arrays.copyOf(Object[] original, int newLength) 方法分配一個新的數組並賦給變量 r,長度是原先 r 數組的1.5倍左右(newCap = cap + (cap >> 1) + 1),而後繼續將集合中後面迭代出來的元素按順序保存到數組 r 中。直到 r 再次滿了,那麼再重複上面的步驟繼續分配空間。
最後一行 return (i == r.length) ? r : Arrays.copyOf(r, i); 的意思是,若按照前面每次分配 1.5 倍新空間的增加方式,若最終迭代完畢後整好填滿了數組 r,那麼直接返回 r 就能夠了;若新分配的空間並無徹底被用完集合就遍歷結束了,那麼再從新分配一個與真實元素數量相同長度的數組來保存 r 的數據,並返回給調用者。關於這一點,你們看一下 java.util.Arrays.copyOf(Object[] original, int newLength) 的源碼就明白了。
私有函數 hugeCapacity(int minCapacity) 的做用是檢查分配的元素數量有沒有超過上限(MAX_ARRAY_SIZE)。
5.toArray(T[] a) 方法
1 @SuppressWarnings("unchecked") 2 public <T> T[] toArray(T[] a) { 3 // Estimate size of array; be prepared to see more or fewer elements 4 int size = size(); 5 T[] r = a.length >= size ? a : 6 (T[])java.lang.reflect.Array 7 .newInstance(a.getClass().getComponentType(), size); 8 Iterator<E> it = iterator(); 9 10 for (int i = 0; i < r.length; i++) { 11 if (! it.hasNext()) { // fewer elements than expected 12 if (a == r) { 13 r[i] = null; // null-terminate 14 } else if (a.length < i) { 15 return Arrays.copyOf(r, i); 16 } else { 17 System.arraycopy(r, 0, a, 0, i); 18 if (a.length > i) { 19 a[i] = null; 20 } 21 } 22 return a; 23 } 24 r[i] = (T)it.next(); 25 } 26 // more elements than expected 27 return it.hasNext() ? finishToArray(r, it) : r; 28 }
與 toArray() 方法相同,也是用於將集合轉換爲數組。
這裏須要注意的兩點:
1) 若參數 a 的長度小於集合的長度,此函數會在內部從新分配數組用於返回值,因此調用者的入參不會被改變。因此建議調用者使用其返回值而不是入參做爲後續處理的數據源。
2) 若參數 a 的長度大於集合的長度,則參數 a 比此集合多出來的部分會被賦爲 null。
你們不妨運行下面的代碼,經過修改 len 來控制數組 ary0 的長度進行測試,觀察運行結果。
1 public static void main(String[] args) { 2 AbstractCollection<String> c = new ArrayList<String>(); 3 c.add("a"); 4 c.add("b"); 5 c.add("c"); 6 c.add("d"); 7 int len = 5; 8 String [] ary0 = new String [len]; 9 ary0[4] = "eee"; 10 String [] ary1 = c.toArray(ary0); 11 print(ary0); 12 System.out.println("ary1.length = " + ary1.length); 13 print(ary1); 14 } 15 16 private static void print(String [] ary) { 17 for (int i = 0; i < ary.length; i++) { 18 System.out.println("ary[" + i + "]" + ary[i]); 19 } 20 }
6.remove(Object o) 方法
1 public boolean remove(Object o) { 2 Iterator<E> it = iterator(); 3 if (o==null) { 4 while (it.hasNext()) { 5 if (it.next()==null) { 6 it.remove(); 7 return true; 8 } 9 } 10 } else { 11 while (it.hasNext()) { 12 if (o.equals(it.next())) { 13 it.remove(); 14 return true; 15 } 16 } 17 } 18 return false; 19 }
此方法用於從集合中刪除一個與參數 o 相同的元素。
這個方法的實現與 contains(Object o) 方法十分類似,只不過是多調用了一下迭代器中的 remove() 方法而已。
一樣須要注意這裏比較對象使用的是 equals() 方法,所以能夠經過重寫對象 equals() 方法的方式來自定義刪除規則。
7.containsAll(Collection<?> c) 方法
1 public boolean containsAll(Collection<?> c) { 2 for (Object e : c) 3 if (!contains(e)) 4 return false; 5 return true; 6 }
此方法用來測試當前集合是否包含集合 c 中的每個元素。
實現方式很簡單,迭代集合 c,測試其每個元素是否被包含在當前的集合中,如有任何一個元素不包含在當前集合中,則返回 false。
8.addAll(Collection<? extends E> c) 方法
1 public boolean addAll(Collection<? extends E> c) { 2 boolean modified = false; 3 for (E e : c) 4 if (add(e)) 5 modified = true; 6 return modified; 7 }
此方法將集合 c 中的每個元素依次添加到本集合中。
但請注意,只要集合 c 中的任何一個元素成功添加到了當前集合中,即便其它元素所有添加失敗了,此方法也會返回 true。因此官方 API 文檔對於返回值給出的解釋是:若是此 collection 因爲調用而發生更改,則返回 true。
9.removeAll(Collection<?> c) 方法
1 public boolean removeAll(Collection<?> c) { 2 Objects.requireNonNull(c); 3 boolean modified = false; 4 Iterator<?> it = iterator(); 5 while (it.hasNext()) { 6 if (c.contains(it.next())) { 7 it.remove(); 8 modified = true; 9 } 10 } 11 return modified; 12 }
此方法用於從當前的集合中移除全部與 c 集合相同成員的成員。此方法的做用並非無條件清空本集合,若需如此作,要調用 clear() 方法。
實現很是簡單,迭代遍歷,只要包含相同的成員就 remove() 掉。
這裏的 Objects.requireNonNull(c); 用於判斷入參 c 是否爲 null,若爲 null 就跑出空指針異常。java.util.Objects 類是從 JDK 1.7 版本開始提供的。
10.retainAll(Collection<?> c) 方法
1 public boolean retainAll(Collection<?> c) { 2 Objects.requireNonNull(c); 3 boolean modified = false; 4 Iterator<E> it = iterator(); 5 while (it.hasNext()) { 6 if (!c.contains(it.next())) { 7 it.remove(); 8 modified = true; 9 } 10 } 11 return modified; 12 }
此方法與 removeAll(Collection<?> c) 方法偏偏相反,僅保留當前集合中與集合 c 中相同的元素,也就是說將本集合中與集合 c 中的不一樣的元素都刪掉。
實現方式與 removeAll(Collection<?> c) 方法徹底相同,只有測試包含的條件是相反的。
11.clear() 方法
1 public void clear() { 2 Iterator<E> it = iterator(); 3 while (it.hasNext()) { 4 it.next(); 5 it.remove(); 6 } 7 }
無條件清空當前集合。迭代刪除每個元素便可。