Java 容器---實(shí)現(xiàn)

“工欲善其事,必先利其器”。Java 容器就是我們開發(fā)中的利器。

然而,之前在開發(fā)中使用僅僅是容器的一小部分。這次從源碼的角度進(jìn)行深入的理解,一點(diǎn)總結(jié)分享給大家。

這里只列舉了非阻塞式的容器;阻塞式的容器,會(huì)在后面的并發(fā)篇補(bǔ)。

如果有什么理解不對(duì)的地方,歡迎大家在評(píng)論中指正~

ArrayList


實(shí)現(xiàn): 數(shù)組實(shí)現(xiàn)

線程安全: 非線性安全,fail-fast 機(jī)制保護(hù)

容量: 初始容量為10;隨后每次增加都會(huì)變成之前的1.5倍(批量添加除外)。源碼如下:

private void grow(int minCapacity) {
    int oldCapacity = elementData.length;
    // 這里是擴(kuò)容的關(guān)鍵
    int newCapacity = oldCapacity + (oldCapacity >> 1);
    // 批量添加,也有可能會(huì)直接等于相加之后的容量
    if(newCapacity - minCapacity < 0) newCapacity = minCapacity;
    if(newCapacity - MAX_ARRAY_SIZE > 0) newCapacity = hugeCapacity(minCapacity);
    elementData = Arrays.copyOf(elementData, newCapacity);
}

效率:

操作 平均 最好 最差
size() O(1) O(1) O(1)
isEmpty() O(1) O(1) O(1)
contains(Object o) O(n) O(1) O(n)
indexOf(Object o) O(n) O(1) O(n)
lastIndexOf(Object o) O(n) O(1) O(n)
toArray() O(n) O(n) O(n)
get(int index) O(1) O(1) O(1)
set(int index, E e) O(1) O(1) O(1)
add(E e) O(1) O(1) O(1)
add(int index, E e) O(n) O(1) O(n)
remove(int index) O(n) O(1) O(n)
remove(Object o) O(n) O(1) O(n)
clear() O(n) O(n) O(n)
addAll(Collection c) O(m) O(m) O(m)
removeAll(Collection c) O(m*n) O(m) O(m*n)
retainAll(Collection c) O(m*n) O(m) O(m*n)

除了模型中的操作外還有一些特殊的操作:

// 縮小容器的容量到元素的數(shù)量
public void trimToSize();
// 確保容量能覆蓋 minCapacity 個(gè)元素
public synchronized void ensureCapacity(int minCapacity) ;

Vector


實(shí)現(xiàn): 數(shù)組

線程安全: 是;fast-fail 保護(hù)

容量: 初始容量為10;根據(jù)指定的擴(kuò)容數(shù)量或者*2(批量添加除外);核心代碼:

private void grow(int minCapacity) {
    int oldCapacity = elementData.length;
    // capacityIncrement 是構(gòu)造函數(shù)中指定的值, 默認(rèn)為0
    int newCapacity = oldCapacity + ((capacityIncrement > 0) ? capacityIncrement : oldCapacity);
    if (newCapacity - minCapacity < 0) newCapacity = minCapacity;
    if (newCapacity - MAX_ARRAY_SIZE > 0)  newCapacity = hugeCapacity(minCapacity);
    elementData = Arrays.copyOf(elementData, newCapacity);
}

效率:

效率與ArrayList相似,但是每一個(gè)操作都需要獲取鎖,時(shí)間開銷要比ArrayList大。

操作 平均 最好 最差
add(E e) O(1) O(1) O(1)
add(int index, E e) O(n) O(1) O(n)
get(int index) O(1) O(1) O(1)
remove(int index) O(n) O(1) O(n)
remove(Object o) O(n) O(1) O(n)
removeAll(Collection c) O(n) O(n) O(n)
retainAll(Collection) O(n) O(n) O(n)
indexOf(Object) O(n) O(1) O(n)

Stack


實(shí)現(xiàn): Vector

線程安全: 是;fast-fail保護(hù)

容量: 同Vector

效率:

操作 平均 最好 最差
push(E e) O(1) O(1) O(1)
pop() O(1) O(1) O(1)
peek() O(1) O(1) O(1)
empty() O(1) O(1) O(1)

LinkedList


實(shí)現(xiàn): 雙指針鏈表

線程安全:否;fast-fail保護(hù)

容量: 有size();無容量

效率:

作為 List 來說:

操作 平均 最好 最差
size() O(1) O(1) O(1)
isEmpty() O(1) O(1) O(1)
contains O(n) O(1) O(n)
add(E e) O(1) O(1) O(1)
remove(Object o) O(n) O(1) O(n)
containsAll(Collection c) O(m*n) O(m) O(m*n)
toArray() O(n) O(n) O(n)
removeAll(Collection c) O(m*n) O(m) O(m*n)
retainAll(Collection c) O(m*n) O(m) O(m*n)
removeAll(Collection c) O(m*n) O(m) O(m*n)
retainAll(Collection c) O(m*n) O(m) O(m*n)
clear() O(1) O(1) O(1)
get(int index) O(n) O(1) O(n)
set(int index,E e) O(n) O(1) O(n)
add(int index, E e) O(n) O(1) O(n)
remove(int index) O(n) O(1) O(n)
indexOf(Object o) O(n) O(1) O(n)
lastIndexOf(Object o) O(n) O(1) O(n)

作為 Queue 來說:

操作 平均 最好 最差
add(E e) O(1) O(1) O(1)
offer(E e) O(1) O(1) O(1)
remove() O(1) O(1) O(1)
poll() O(1) O(1) O(1)
element() O(1) O(1) O(1)
peek() O(1) O(1) O(1)

作為 Deque 來說:

操作 平均 最好 最差
xxxFisrt O(1) O(1) O(1)
xxxLast O(1) O(1) O(1)
removeFirstOccurrence(Object o) O(n) O(1) O(n)
removeLastOccurrence(Object o) O(n) O(1) O(n)
pop O(1) O(1) O(1)
push O(1) O(1) O(1)

PriorityQueue


特征: 容器中元素的 出順序 不是元素的 插入順序,而是一個(gè)大小順序中的最值。

實(shí)現(xiàn)方式: 小頂堆

線程安全: 否;fail-fast保護(hù)

容量: 默認(rèn)初始容量11;容量小的時(shí)候*2,容量大的時(shí)候+50%。核心代碼如下:

private void grow(int minCapacity) {
int oldCapacity = queue.length;
// 小于64的時(shí)候增倍;大于64的時(shí)候增加50%
int newCapacity = oldCapacity + ((oldCapacity < 64) ? (oldCapacity + 2) : (oldCapacity >> 1));
if (newCapacity - MAX_ARRAY_SIZE > 0) newCapacity = hugeCapacity(minCapacity);
// queue是存放元素的數(shù)組
queue = Arrays.copyOf(queue, newCapacity);

效率:

如果只是用到確定數(shù)量個(gè)元素的情況下,它是比列表排序的效率要高的。隨機(jī)數(shù)列的排序平均都要O(n*log(n))了,而它最高的復(fù)雜度僅為O(log(n))

但是,如果所有元素都要遍歷一次取出來,那效率與就跟堆排序是一樣的O(n*log(n))。

操作 平均 最好 最差
add(E e) O(log(n)) O(1) O(log(n))
offer(E e) O(log(n)) O(1) O(log(n))
remove() O(log(n)) O(log(n)) O(log(n))
poll() O(log(n)) O(log(n)) O(log(n))
peek() O(1) O(1) O(1)
element() O(1) O(1) O(1)
toArray() O(n) O(n) O(n)
clear() O(n) O(n) O(n)

HashSet


特征: 高效率的集合容器

實(shí)現(xiàn): HashMap

線程安全: 否;failfast保護(hù)

容量: 同HashMap

效率:同HashMap

LinkedHashSet


特征: 高效率的集合容器,能保存插入順序

實(shí)現(xiàn):LinkedHashMap

線程安全:否; failfast保護(hù)

容量: 同LinkedHashMap

效率: 同LinkedHashMap

TreeSet


特征: 帶全排序的集合容器

實(shí)現(xiàn):TreeMap

線程安全:否;failfast保護(hù)

容量:無

效率:同TreeMap

HashMap


特征:高效率的映射表

實(shí)現(xiàn): 開散列

線程安全:否;fail-fast保護(hù)

是否支持null: 是

容量: 默認(rèn)初始容量16;容量總是2的冪;默認(rèn)加載因子0.75;擴(kuò)容后的容量由當(dāng)前容量和加載因子決定,這里的擴(kuò)容代碼不是很直接,需要聯(lián)系幾個(gè)方法來看:

// 添加一組映射的時(shí)候
void addEntry(int hash, K key, V value, int bucketIndex) {
    // 容量超過threshold并且對(duì)應(yīng)的hash值存在在沖突的時(shí)候,開始擴(kuò)容
    if ((size >= threshold) && (null != table[bucketIndex])) {
        // 擴(kuò)展為之前的2倍
        resize(2 * table.length);
        hash = (null != key) ? hash(key) : 0;
        bucketIndex = indexFor(hash, table.length);
    }
    createEntry(hash, key, value, bucketIndex);
}

// 這個(gè)方法的作用在于將數(shù)組擴(kuò)展到相應(yīng)的容量,
// 并把原數(shù)組中的元素全部重新計(jì)算位置,轉(zhuǎn)移到新數(shù)組中
// 重新計(jì)算下一個(gè)threshold
void resize(int newCapacity) {
    Entry[] oldTable = table;
    int oldCapacity = oldTable.length;
    if (oldCapacity == MAXIMUM_CAPACITY) {
        threshold = Integer.MAX_VALUE;
        return;
    }
    Entry[] newTable = new Entry[newCapacity];
    transfer(newTable, initHashSeedAsNeeded(newCapacity));
    table = newTable;
    threshold = (int)Math.min(newCapacity * loadFactor, MAXIMUM_CAPACITY + 1);
}

// 這個(gè)方法在容器第一次添加元素的時(shí)候調(diào)用,使用toSize做參數(shù)的目的是避免第一次添加元素的時(shí)候多次擴(kuò)容
private void inflateTable(int toSize) {
    int capacity = roundUpToPowerOf2(toSize);
    threshold = (int) Math.min(capacity * loadFactor, MAXIMUM_CAPACITY + 1);
    table = new Entry[capacity];
    initHashSeedAsNeeded(capacity);
}

效率:

操作 平均 最好 最差
size() O(1) O(1) O(1)
isEmpty() O(1) O(1) O(1)
get O(1) O(1) O(n)
getEntry(Object key) O(1) O(1) O(n)
put(K key, V value) O(1) O(1) O(n)
remove O(1) O(1) O(n)
clear O(n) O(n) O(n)
containsValue O(n) O(1) O(n)
putAll(Collection c) O(m) O(m) O(m*n)

LinkedHashMap


特征: 在HashMap的基礎(chǔ)上添加了,特定規(guī)則的順序保存

實(shí)現(xiàn):開散列 + 雙指針鏈表環(huán)

線程安全: 否;fast-fail保護(hù)

是否支持null: 是

容量: 同HashMap

特征:它在HashMap的基礎(chǔ)上,對(duì)Entry的封裝中加了兩個(gè)指針記錄順序。也就是說,這里既有散列保證隨機(jī)訪問效率,又有鏈表記錄順序。

這里的順序不是指大小順序。而是插入順序,或者訪問次數(shù)的順序。所以它可以用來做LRU緩存容器,不過在此之前需要對(duì)他做一些封裝,需要定義緩存的體積和重寫一些方法:

//這里的accessOrder要設(shè)置為true
public LinkedHashMap(int initialCapacity, float loadFactor, boolean accessOrder) {
}
// 緩存容量
private static final int MAX_ENTRIES = 100;
// 設(shè)置為超容量即刪除
protected boolean removeEldestEntry(Map.Entry eldest) {
    return size() > MAX_ENTRIES;
}

TreeMap


特征: 全排序的映射表

實(shí)現(xiàn):紅黑樹

線程安全: 否,fastfail保護(hù)

是否支持null:是

容量:無

特征:訪問的時(shí)候根據(jù)二叉查找樹來查找,插入,刪除的時(shí)候需要根據(jù)紅黑樹的規(guī)則來平衡樹。

效率:

操作 平均 最好 最差
size() O(1) O(1) O(1)
isEmpty() O(1) O(1) O(1)
containsKey(Object key) O(log(n)) O(1) O(log(n))
containsValue(Object value) O(n) O(1) O(n)
get(Object key) O(log(n)) O(log(n)) O(log(n))
put(K key, V value) O(log(n)) O(log(n)) O(log(n))
remove(Object key) O(log(n)) O(log(n)) O(log(n))
clear() O(1) O(1) O(1)
firstKey() O(log(n)) O(log(n)) O(log(n))
lastKey() O(log(n)) O(log(n)) O(log(n))
putAll(Map map) O(log(n)) O(log(n)) O(log(n))
firstEntry() O(log(n)) O(log(n)) O(log(n))
lastEntry() O(log(n)) O(log(n)) O(log(n))
pollFirstEntry() O(log(n)) O(log(n)) O(log(n))
pollLastEntry() O(log(n)) O(log(n)) O(log(n))
lowerXXX(K key) O(log(n)) O(log(n)) O(log(n))
floorXXX(K key) O(log(n)) O(log(n)) O(log(n))
ceilingXXX(K key) O(log(n)) O(log(n)) O(log(n))
higherXXX(K key) O(log(n)) O(log(n)) O(log(n))

Hashtable


特征:線程安全的高效映射表

實(shí)現(xiàn): 開散列

線程安全: 是;fail-fast保護(hù)

是否支持null:

容量:默認(rèn)初始容量11;加載因子0.75;由加載因子決定擴(kuò)容時(shí)機(jī),默認(rèn)size超過容量的0.75需要擴(kuò)容。核心代碼如下:

public synchronized V put(K key, V value) {
    // ...
    // 容器中元素?cái)?shù)量超過threshold時(shí),擴(kuò)容并重新計(jì)算hash,然后再添加元素
    if (count >= threshold) {
        // Rehash the table if the threshold is exceeded
        rehash();
        tab = table;
        hash = hash(key);
        index = (hash & 0x7FFFFFFF) % tab.length;
    }
    // ...
}

protected void rehash() {
    int oldCapacity = table.length;
    Entry<K,V>[] oldMap = table;
    // 新容量是舊容量的2倍
    int newCapacity = (oldCapacity << 1) + 1;
    if (newCapacity - MAX_ARRAY_SIZE > 0) {
        if (oldCapacity == MAX_ARRAY_SIZE)
            // Keep running with MAX_ARRAY_SIZE buckets
            return;
        newCapacity = MAX_ARRAY_SIZE;
    }
    Entry<K,V>[] newMap = new Entry[newCapacity];
    modCount++;
    // 這里計(jì)算新的閾值
    threshold = (int)Math.min(newCapacity * loadFactor, MAX_ARRAY_SIZE + 1);
    boolean rehash = initHashSeedAsNeeded(newCapacity);
    table = newMap;
    // 然后重新計(jì)算hash對(duì)應(yīng)的位置
    for (int i = oldCapacity ; i-- > 0 ;) {
        for (Entry<K,V> old = oldMap[i] ; old != null ; ) {
            Entry<K,V> e = old;
            old = old.next;
            if (rehash) {
                e.hash = hash(e.key);
            }
            int index = (e.hash & 0x7FFFFFFF) % newCapacity;
            e.next = newMap[index];
            newMap[index] = e;
        }
    }
}


效率:

操作 平均 最好 最差
size() O(1) O(1) O(1)
isEmpty() O(1) O(1) O(1)
get O(1) O(1) O(n)
getEntry(Object key) O(1) O(1) O(n)
put(K key, V value) O(1) O(1) O(n)
remove O(1) O(1) O(n)
clear O(n) O(n) O(n)
containsValue O(n) O(1) O(n)
putAll(Collection c) O(m) O(m) O(m*n)

WeakHashMap


特征:
它的Map.Entry類設(shè)計(jì)的比較特殊繼承了弱引用類。key是作為弱引用持有的對(duì)象。這意味著容器中的對(duì)象在沒有外部引用持有的時(shí)候隨時(shí)都有可能被GC回收。所以它可以被用來做緩存。

    private static class Entry<K,V> extends WeakReference<Object> implements Map.Entry<K,V> {
        V value;
        int hash;
        Entry<K,V> next;

        /**
         * Creates new entry.
         */
        Entry(Object key, V value,
              ReferenceQueue<Object> queue,
              int hash, Entry<K,V> next) {
            // key是弱引用持有的對(duì)象
            super(key, queue);
            this.value = value;
            this.hash  = hash;
            this.next  = next;
        }
    }

實(shí)現(xiàn): 開散列 + WeakReference + ReferenceQueue

線程安全: 否; fast-fail保護(hù)

是否支持null:是

容量:默認(rèn)初始容量16;默認(rèn)加載因子0.75;容量總是2的冪,每次擴(kuò)容都*2;如果在擴(kuò)容過程中發(fā)現(xiàn)大量的元素被回收,可能會(huì)終止擴(kuò)容,繼續(xù)使用之前的容量。核心代碼如下:

public V put(K key, V value) {
    // ...
    tab[i] = new Entry<>(k, value, queue, h, e);
    if (++size >= threshold)
        resize(tab.length * 2);
}

void resize(int newCapacity) {
    Entry<K,V>[] oldTable = getTable();
    int oldCapacity = oldTable.length;
    if (oldCapacity == MAXIMUM_CAPACITY) {
        threshold = Integer.MAX_VALUE;
        return;
    }
    Entry<K,V>[] newTable = newTable(newCapacity);
    boolean oldAltHashing = useAltHashing;
    useAltHashing |= sun.misc.VM.isBooted() &&
            (newCapacity >= Holder.ALTERNATIVE_HASHING_THRESHOLD);
    boolean rehash = oldAltHashing ^ useAltHashing;
    transfer(oldTable, newTable, rehash);
    table = newTable;
    /*
     * 如果在擴(kuò)容過程中刪除的元素非常的多,填充數(shù)不到閾值的一半的時(shí)候
     * 使用回之前容量的數(shù)組
     */
    if (size >= threshold / 2) {
        threshold = (int)(newCapacity * loadFactor);
    } else {
        expungeStaleEntries();
        transfer(newTable, oldTable, false);
        table = oldTable;
    }
}

/** Transfers all entries from src to dest tables */
private void transfer(Entry<K,V>[] src, Entry<K,V>[] dest, boolean rehash) {
    for (int j = 0; j < src.length; ++j) {
        Entry<K,V> e = src[j];
        src[j] = null;
        while (e != null) {
            Entry<K,V> next = e.next;
            Object key = e.get();
            // 注意,這里在轉(zhuǎn)移元素的過程中會(huì)刪除一些已經(jīng)被垃圾回收的元素
            if (key == null) {
                e.next = null;  // Help GC
                e.value = null; //  "   "
                size--;
            } else {
                if (rehash) {
                    e.hash = hash(key);
                }
                int i = indexFor(e.hash, dest.length);
                e.next = dest[i];
                dest[i] = e;
            }
            e = next;
        }
    }
}

效率:

操作 平均 最好 最差
size() O(n) O(n) O(n)
isEmpty() O(n) O(n) O(n)
get O(1) O(1) O(n)
getEntry(Object key) O(1) O(1) O(n)
put(K key, V value) O(1) O(1) O(n)
remove O(1) O(1) O(n)
clear O(n) O(n) O(n)
containsValue O(n) O(1) O(n)
putAll(Collection c) O(m) O(m) O(m*n)

IdentityHashMap


特征: 使用“引用相等”而非equals相等。只有兩個(gè) key 完全是同一個(gè)對(duì)象的時(shí)候才判定為相等。

實(shí)現(xiàn): 閉散列;沒有用Map.Entry類,數(shù)組偶數(shù)位置放 key,奇數(shù)位置放 value

線程安全: 否;fast-fail保護(hù)

是否支持null:是

容量:默認(rèn)初始容量32;加載因子2/3;容量一定為2的冪;核心代碼:

    public V put(K key, V value) {
        // ...
        // 超過閾值,擴(kuò)容
        if (++size >= threshold)
            resize(len); // len == 2 * current capacity.
        // ...
    }

    private void resize(int newCapacity) {
        // assert (newCapacity & -newCapacity) == newCapacity; // power of 2
        // 原容量*2
        int newLength = newCapacity * 2;

        Object[] oldTable = table;
        int oldLength = oldTable.length;
        // 這里重新計(jì)算閾值
        if (oldLength == 2*MAXIMUM_CAPACITY) { // can't expand any further
            if (threshold == MAXIMUM_CAPACITY-1)
                throw new IllegalStateException("Capacity exhausted.");
            threshold = MAXIMUM_CAPACITY-1;  // Gigantic map!
            return;
        }
        if (oldLength >= newLength)
            return;

        Object[] newTable = new Object[newLength];
        threshold = newLength / 3;
        // 重新計(jì)算位置, 由于這是閉散列,需要對(duì)所有元素重新計(jì)算
        for (int j = 0; j < oldLength; j += 2) {
            Object key = oldTable[j];
            if (key != null) {
                Object value = oldTable[j+1];
                oldTable[j] = null;
                oldTable[j+1] = null;
                int i = hash(key, newLength);
                while (newTable[i] != null)
                    i = nextKeyIndex(i, newLength);
                newTable[i] = key;
                newTable[i + 1] = value;
            }
        }
        table = newTable;
    }

效率:

操作 平均 最好 最差
size() O(1) O(1) O(1)
isEmpty() O(1) O(1) O(1)
get O(1) O(1) O(n)
getEntry(Object key) O(1) O(1) O(n)
put(K key, V value) O(1) O(1) O(n)
remove O(1) O(1) O(n)
clear O(n) O(n) O(n)
containsKey(Object key) O(1) O(1) O(n)
containsValue(Object value) O(n) O(1) O(n)
putAll(Collection c) O(m) O(m) O(m*n)
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容