Java集合框架原始碼分析
在JDK的原始碼裡有一個RandomAccess介面,這個介面沒有任何方法需要實現,那麼它是幹什麼用的呢?
public interface RandomAccess {
}
官方文件解釋如下:
介面RandomAccess被List實現用來指示它們支援快速的(通常是恆定的)隨機訪問。此介面的主要目的是允許通用演算法改變其行為,以便在應用於隨機或順序訪問列表時提供良好的效能。
用於處理隨機訪問列表(如ArrayList)的最佳演算法可應用於順序訪問列表(如LinkedList)時產生二次行為。鼓勵通用列表演算法檢查給定列表是否是此介面的一個例項,然後應用演算法,如果將其應用於順序訪問列表將提供較差的效能,並在必要時更改其行為以保證可接受的效能。
認識到隨機訪問和順序訪問之間的區別通常是模糊的。例如,一些List實現在漸近線性訪問時間的情況下獲得巨大的訪問時間,但在實踐中訪問時間不變。這樣的List實現通常應該實現這個介面。作為一個經驗法則,如果對於典型的類例項,List實現應該實現這個介面。
根本原因是:
for(int i = 0,n = list.size(); i <n; i ++)
list.get(ⅰ);
執行速度比這個迴圈更快:
for(Iterator i = list.iterator(); i.hasNext();)
i.next();
我自己的理解是這樣的。
容器一般情況下有兩種實現方式,一種是陣列,一種是連結串列,對於陣列來說,隨機訪問(下標訪問)是最快的,但是對於連結串列的實現形式來說,隨機訪問(下標訪問)是很慢的,因為需要從頭結點逐步訪問到對應的下標結點。
所以為了提高訪問效率,通過這個介面來區分是否當前類允許隨機訪問。
我們看下最常用的兩個類ArrayList和LinkedList的類宣告。
ArrayList:
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{...}
LinkedList:
public class LinkedList<E>
extends AbstractSequentialList <E>
implements List<E>, Deque<E>, Cloneable, java.io.Serializable
{...}
ArrayList實現的其中一個介面就是RandomAccess,但是LinkedList沒有,所以ArrayList的隨機訪問效率要高,實際上也是這樣的,因為ArrayList是以陣列儲存元素的。
Collection框架中的工具類Collections的binarySearch()方法就是藉助這個特性提高效能的。
public class Collections {
...
public static <T>
int binarySearch(List<? extends Comparable<? super T>> list, T key) {
if (list instanceof RandomAccess || list.size()<BINARYSEARCH_THRESHOLD)
return Collections.indexedBinarySearch(list, key);
else
return Collections.iteratorBinarySearch(list, key);
}
...
}
接著我們看下indexedBinarySearch和iteratorBinarySearch方法的原始碼:
indexedBinarySearch
private static <T>
int indexedBinarySearch(List<? extends Comparable<? super T>> list, T key) {
int low = 0;
int high = list.size()-1;
while (low <= high) {
int mid = (low + high) >>> 1;
//直接根據下標獲取元素值
Comparable<? super T> midVal = list.get(mid);
int cmp = midVal.compareTo(key);
if (cmp < 0)
low = mid + 1;
else if (cmp > 0)
high = mid - 1;
else
return mid; // key found
}
return -(low + 1); // key not found
}
iteratorBinarySearch
private static <T>
int iteratorBinarySearch(List<? extends Comparable<? super T>> list, T key)
{
int low = 0;
int high = list.size()-1;
ListIterator<? extends Comparable<? super T>> i = list.listIterator();
while (low <= high) {
int mid = (low + high) >>> 1;
//根據迭代器查詢元素
Comparable<? super T> midVal = get(i, mid);
int cmp = midVal.compareTo(key);
if (cmp < 0)
low = mid + 1;
else if (cmp > 0)
high = mid - 1;
else
return mid; // key found
}
return -(low + 1); // key not found
}
indexedBinarySearch方法是以下標的形式訪問元素,而iteratorBinarySearch是以迭代器的形式訪問,符合我們之前的結論。