在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是以迭代器的形式訪問,符合我們之前的結論。