Java集合總結(二):Map和Set


集合類的架構圖:

HashMap

  • 內部維護一個鏈表數組做哈希表,默認大小為16,最大值可以為2^30,默認負載因子0.75。
  • 可以通過構造方法指定初始大小和負載因子,當鍵值對個數大於等於臨界值threshold(數組當前大小和負載因子的乘積)時對數組進行擴容,擴容策略為當前數組大小乘以2。
  • 數組的每一項都是一個鏈表,鏈表的每個結點(靜態內部類Entry)都是鍵值對,並緩存了key的hash值。
  • key 和value都可以為null,key為null時結點存儲在hash表數組下標為0的位置。

put過程:

    public V put(K key, V value) {
        if (table == EMPTY_TABLE) {
            inflateTable(threshold);
        }
        if (key == null)
            return putForNullKey(value);
        int hash = hash(key);
        int i = indexFor(hash, table.length);
        for (Entry<K,V> e = table[i]; e != null; e = e.next) {
            Object k;
            if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
                V oldValue = e.value;
                e.value = value;
                e.recordAccess(this);
                return oldValue;
            }
        }

        modCount++;
        addEntry(hash, key, value, i);
        return null;
    }
  • 通過key的hashcode計算出一個內部的hash值,然后用這個hash值對哈希表大小取余(算法為h & (length-1),此處可以體現hash表length擴容策略為指數方式的優勢)定位到哈希表的位置,然后遍歷該位置的鏈表,當遇到相等的key時,替換原來的value並將原來的value返回
  • 如果沒找着key相同的記錄,就在相應位置添加新的鏈表結點,並將原來該位置的鏈表鏈接到此節點后,此節點作為頭結點,當size大於閾值,則擴容到原來數組大小的兩倍

HashMap不是線程安全的,多線程環境下可能造成死循環(對hash表擴容后transfer數據時發生)或者丟失數據(hash沖突后添加新節點到鏈表時發生)。

 

HashSet
HashSet通過內嵌一個HashMap對象的方式來實現,通過HashMap的key來存儲,value都是相同的一個空Object()對象。與HashMap一樣,要求需要存儲的key實現hashcode和equals方法,且與HashMap具備同樣的初始大小和擴容策略。

 

TreeMap

紅黑樹:一種大致平衡的二叉查找樹,大致平衡是為了在保持較高檢索效率的同時還不需要頻繁調整,從而保持了統計上的性能。

  • TreeMap內部使用了紅黑樹來實現,維護其根節點,每個key-value都內嵌於其中一個節點(Entry),同時Entry還具有left、right、parent以及color屬性用以維持其樹形結構。
  • 結點之間按key有序,需要key實現comparable接口或者在構造方法中傳入一個比較器comparator。
  • 迭代時按key排序,保存時會使用key的比較結果對key進行排重,只要比較結果相同就會被認為是同一份,此時保存的key值為第一次put的key,value為第二次put進去的value
  • 通過key get時,搜索二叉查找樹,找到匹配的返回其value,找不到返回null
  • 通過value獲取時,遍歷所有節點搜索
  • TreeMap實現了SortedMap和NavigableMap接口,可以方便的根據鍵的順序進行查找,如第一個、最后一個、某一范圍的鍵、鄰近鍵等。
  • 根據鍵保存、查找、刪除的效率比較高,為O(h),h為樹的高度,在樹平衡的情況下,h為log2(N),N為節點數。
  • TreeSet
  • 內部持有一個TreeMap,類似HashSet,沒有重復元素,添加刪除判斷元素是否存在效率較高,為O(log2N),N為元素個數
  • 有序,可以方便的根據順序進行查找和操作,如第一個,最后一個,某一取值范圍,某一值的近鄰元素。

 

LinkedHashMap

  • LinkedHashMap是HashMap的子類,內部有一個雙向鏈表維護鍵值對的順序,每個鍵值對既位於哈希表中,也位於這個雙向鏈表中。
  • 雙向鏈表的結點LinkedHashMap.Entry繼承自HashMap.Entry,添加了before和after兩個引用參數,同時重寫了HashMap.Entry的recordAccess和recordRemoval方法以維護和hash表中節點的關系。
  • LinkedHashMap支持兩種順序,一種是插入順序,另一種是訪問順序,默認情況下按插入有序,構造方法中accessOrder設為true的時候按訪問順序,可以用來實現LRU緩存(最近最少使用)

 

LinkedHashSet
LinkedHashMap也有一個對應的Set接口的實現類LinkedHashSet。LinkedHashSet是HashSet的子類,但它內部的Map的實現類是LinkedHashMap,所以它也可以保持插入順序

 

EnumMap
內部使用數組實現,構造方法需要傳入類型信息。允許值為null,為了區分null和沒有值,用一個靜態全局唯一的new Integer(0)值來作為沒有值

EnumSet
內部使用位向量實現,是一個抽象類,不能直接通過new關鍵字來新建,必須使用類似於noneOf的其他工廠方法方法創建一個指定枚舉類型的set,實際創建的對象是EnumSet的子類RegularEnumSet或JumboEnumSet。

具體子類類型根據傳入的枚舉類型枚舉值的數量來決定:

  • 小於等於64返回維護一個long變量(long為64位)作為位向量的子類RegularEnumSet
  • 大於64返回一個內部維護long數組作為位向量的子類JumboEnumSet

下面是一些工廠方法:

// 初始集合包括指定枚舉類型的所有枚舉值
<E extends Enum<E>> EnumSet<E> allOf(Class<E> elementType)
// 初始集合包括枚舉值中指定范圍的元素
<E extends Enum<E>> EnumSet<E> range(E from, E to)
// 初始集合包括指定集合的補集
<E extends Enum<E>> EnumSet<E> complementOf(EnumSet<E> s)
// 初始集合包括參數中的所有元素
<E extends Enum<E>> EnumSet<E> of(E e)
<E extends Enum<E>> EnumSet<E> of(E e1, E e2)
<E extends Enum<E>> EnumSet<E> of(E e1, E e2, E e3)
<E extends Enum<E>> EnumSet<E> of(E e1, E e2, E e3, E e4)
<E extends Enum<E>> EnumSet<E> of(E e1, E e2, E e3, E e4, E e5)
<E extends Enum<E>> EnumSet<E> of(E first, E... rest)
// 初始集合包括參數容器中的所有元素
<E extends Enum<E>> EnumSet<E> copyOf(EnumSet<E> s)
<E extends Enum<E>> EnumSet<E> copyOf(Collection<E> c)

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM