(二)一塊兒學 Java Collections Framework 源碼之 AbstractCollection

.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 }

 

無條件清空當前集合。迭代刪除每個元素便可。

相關文章
相關標籤/搜索