1. 程式人生 > >Java集合框架原始碼分析

Java集合框架原始碼分析

在JDK的原始碼裡有一個RandomAccess介面,這個介面沒有任何方法需要實現,那麼它是幹什麼用的呢?

public interface RandomAccess {
}

官方文件解釋如下:

介面RandomAccess被List實現用來指示它們支援快速的(通常是恆定的)隨機訪問。此介面的主要目的是允許通用演算法改變其行為,以便在應用於隨機或順序訪問列表時提供良好的效能。
用於處理隨機訪問列表(如ArrayList)的最佳演算法可應用於順序訪問列表(如LinkedList)時產生二次行為。鼓勵通用列表演算法檢查給定列表是否是此介面的一個例項,然後應用演算法,如果將其應用於順序訪問列表將提供較差的效能,並在必要時更改其行為以保證可接受的效能。

認識到隨機訪問和順序訪問之間的區別通常是模糊的。例如,一些List實現在漸近線性訪問時間的情況下獲得巨大的訪問時間,但在實踐中訪問時間不變。這樣的List實現通常應該實現這個介面。作為一個經驗法則,如果對於典型的類例項,List實現應該實現這個介面。

根本原因是:

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