JDK8-數據結構-Map以及具體的實現(待更新)

Map

存儲一組鍵值對象,提供key到value的映射。
在這裏插入圖片描述

實現類

在這裏插入圖片描述

java.util.EnumMap

key爲枚舉,value爲Object[],使用枚舉的特性,將枚舉的ordinal屬性作爲數組的下標。

  • 增加:put(K key, V value)
public V put(K key, V value) {
    typeCheck(key);
	// 枚舉的ordinal屬性是根據定義枚舉的順序生成的,從0開始
    int index = key.ordinal();
    Object oldValue = vals[index];
    vals[index] = maskNull(value);
    if (oldValue == null)
        size++;
    return unmaskNull(oldValue);
}

java.util.HashMap

數據結構與算法

HashMap用到數組,鏈表(單鏈表),樹(紅黑樹)三種數據結構和哈希算法。

  • 數組:數組的內存是連續型的,所以根據下標可以算出內存地址。

例如:int[10]分配的地址爲1000到1039,int[i]=1000(首地址)+i*4

  • 哈希算法:將Map中的key經過hash函數進行運算之後,讓其通過key,可以直接算出下標,實現通過key達到O(1)的性能
static final int hash(Object key) {
    int h;
    // 異或運算:0^0=0;0^1=1;1^0=1;1^1=0
    return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}

// 與運算:0&0=0;0&1=0;1&0=0;1&1=1
// 下標計算方法,將數組長度和hash值進行與運算,可以保證下標不會越界
int index = ( size - 1) & hash(key)
  • 單鏈表
  • 紅黑樹

當key發生hash衝突時,就需要使用到單鏈表,當鏈表達到一定長度時會轉爲紅黑樹,來提升查詢效率;當紅黑樹的節點在指定範圍時會轉爲單鏈表,具體如下圖:

在這裏插入圖片描述

源碼閱讀

屬性

  • loadFactor:負載因子,用於計算臨界值(threshold)。默認爲0.75
  • threshold:臨界值,用於判斷是否要擴容。計算公式爲size*loadFactor
  • TREEIFY_THRESHOLD:轉紅黑樹臨界值常量,值爲8。判斷是否樹化的條件之一。
  • UNTREEIFY_THRESHOLD:轉鏈表臨界值常量,值爲6。轉爲鏈表的情況之一。
  • MIN_TREEIFY_CAPACITY:轉紅黑樹容量最小值常量,值爲64。判斷是否樹化的條件之一

方法

  • 增加:put(K key, V value)
public V put(K key, V value) {
    return putVal(hash(key), key, value, false, true);
}

// 時間複雜度:O(logn)
// 數組O(1)
// 如果是單鏈表,則爲O(n),但是這個n最大爲8,也可以算是O(1)
// 如果是紅黑樹,則爲O(logn)
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
               boolean evict) {
    Node<K,V>[] tab; Node<K,V> p; int n, i;
    if ((tab = table) == null || (n = tab.length) == 0)
        n = (tab = resize()).length;
    if ((p = tab[i = (n - 1) & hash]) == null) // 不存在hash衝突
        tab[i] = newNode(hash, key, value, null);
    else { // hash衝突
        Node<K,V> e; K k;
        if (p.hash == hash &&
            ((k = p.key) == key || (key != null && key.equals(k)))) // 同一個元素
            e = p;
        else if (p instanceof TreeNode) // 當前節點爲樹節點,使用紅黑樹的插入
            e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
        else { // 插入到鏈表中
            for (int binCount = 0; ; ++binCount) {
                if ((e = p.next) == null) { // 追加在鏈表的尾部
                    p.next = newNode(hash, key, value, null);
                    // 判斷是否達到樹化的臨界值,爲8,也就是單個key的衝突達到8時,滿足樹化的第一個條件
                    // treeifyBin函數內部還會判斷當前數組是否大於64
                    if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
                        treeifyBin(tab, hash);
                    break;
                }
                if (e.hash == hash &&
                    ((k = e.key) == key || (key != null && key.equals(k))))
                    break;
                p = e;
            }
        }
        if (e != null) { // existing mapping for key
            V oldValue = e.value;
            if (!onlyIfAbsent || oldValue == null)
                e.value = value;
            afterNodeAccess(e);
            return oldValue;
        }
    }
    // 判斷是否需要擴容
    ++modCount;
    if (++size > threshold)
        resize();
    afterNodeInsertion(evict);
    return null;
}
  • 刪除:remove(Object key)
public V remove(Object key) {
    Node<K,V> e;
    return (e = removeNode(hash(key), key, null, false, true)) == null ?
        null : e.value;
}

// 時間複雜度:O(logn)
// 數組O(1)
// 如果是單鏈表,則爲O(n),但是這個n最大爲8,也可以算是O(1)
// 如果是紅黑樹,則爲O(logn)
final Node<K,V> removeNode(int hash, Object key, Object value,
                           boolean matchValue, boolean movable) {
    Node<K,V>[] tab; Node<K,V> p; int n, index;
    if ((tab = table) != null && (n = tab.length) > 0 &&
        (p = tab[index = (n - 1) & hash]) != null) {
        // 查找元素
        Node<K,V> node = null, e; K k; V v;
        if (p.hash == hash &&
            ((k = p.key) == key || (key != null && key.equals(k))))
            node = p;
        else if ((e = p.next) != null) {
            if (p instanceof TreeNode)
                node = ((TreeNode<K,V>)p).getTreeNode(hash, key);
            else {
                do {
                    if (e.hash == hash &&
                        ((k = e.key) == key ||
                         (key != null && key.equals(k)))) {
                        node = e;
                        break;
                    }
                    p = e;
                } while ((e = e.next) != null);
            }
        }
        
        if (node != null && (!matchValue || (v = node.value) == value ||
                             (value != null && value.equals(v)))) {
            if (node instanceof TreeNode)
                ((TreeNode<K,V>)node).removeTreeNode(this, tab, movable);
            else if (node == p)
                tab[index] = node.next;
            else
                p.next = node.next;
            ++modCount;
            --size;
            afterNodeRemoval(node);
            return node;
        }
    }
    return null;
}
  • 修改:同增加一致
  • 查詢:get(Object key)
// 時間複雜度:O(logn)
// 數組O(1)
// 如果是單鏈表,則爲O(n),但是這個n最大爲8,也可以算是O(1)
// 如果是紅黑樹,則爲O(logn)
public V get(Object key) {
   Node<K,V> e;
   return (e = getNode(hash(key), key)) == null ? null : e.value;
}

final Node<K,V> getNode(int hash, Object key) {
   Node<K,V>[] tab; Node<K,V> first, e; int n; K k;
   if ((tab = table) != null && (n = tab.length) > 0 &&
       (first = tab[(n - 1) & hash]) != null) {
       if (first.hash == hash && // always check first node
           ((k = first.key) == key || (key != null && key.equals(k))))
           return first;
       if ((e = first.next) != null) {
           if (first instanceof TreeNode)
               return ((TreeNode<K,V>)first).getTreeNode(hash, key);
           do {
               if (e.hash == hash &&
                   ((k = e.key) == key || (key != null && key.equals(k))))
                   return e;
           } while ((e = e.next) != null);
       }
   }
   return null;
}

java.util.TreeMap

使用紅黑樹實現,每個節點都需要比較大小,所以TreeMap需要一個Comparator,如果Comparator爲空,則使用key的自然順序。

  • 增加:put(K key, V value)
// 時間複雜度:O(logn)
public V put(K key, V value) {
    Entry<K,V> t = root;
    // 第一個節點
    if (t == null) {
        compare(key, key); // type (and possibly null) check

        root = new Entry<>(key, value, null);
        size = 1;
        modCount++;
        return null;
    }
	// 計算位置
    int cmp;
    Entry<K,V> parent;
    // split comparator and comparable paths
    Comparator<? super K> cpr = comparator;
    if (cpr != null) {
        do {
            parent = t;
            cmp = cpr.compare(key, t.key);
            if (cmp < 0)
                t = t.left;
            else if (cmp > 0)
                t = t.right;
            else
                return t.setValue(value);
        } while (t != null);
    }
    else {
        if (key == null)
            throw new NullPointerException();
        @SuppressWarnings("unchecked")
            Comparable<? super K> k = (Comparable<? super K>) key;
        do {
            parent = t;
            cmp = k.compareTo(t.key);
            if (cmp < 0)
                t = t.left;
            else if (cmp > 0)
                t = t.right;
            else
                return t.setValue(value);
        } while (t != null);
    }
	// 插入
    Entry<K,V> e = new Entry<>(key, value, parent);
    if (cmp < 0)
        parent.left = e;
    else
        parent.right = e;
    // 紅黑樹的核心,recolor & rotateRight & rotateLeft
    fixAfterInsertion(e);
    size++;
    modCount++;
    return null;
}

java.util.WeakHashMap

java虛擬機有四種引用:強引用(Strong Reference)、軟引用(Soft Reference)、弱引用(Weak Reference)、虛引用(Phantom Reference)四種。對於弱引用關聯的對象只能生存到下一次垃圾收集發生之前。當垃圾收集器工作時,無論當前內存是否足夠,都會回收掉弱引用關聯的對象。

應用場景:緩存

java.util.concurrent.ConcurrentHashMap

實現思路和HashMap,重點是該類支持併發。

源碼閱讀

屬性

  • sizeCtl:
static final <K,V> Node<K,V> tabAt(Node<K,V>[] tab, int i) {
    return (Node<K,V>)U.getObjectVolatile(tab, ((long)i << ASHIFT) + ABASE);
}

static final <K,V> boolean casTabAt(Node<K,V>[] tab, int i,
                                    Node<K,V> c, Node<K,V> v) {
    // 參數1:需要更新的數組, 參數2:數組中第i個元素的偏移量
	// 參數3:數組中第i個元素的期望值,參數4:新值
	// 該函數的操作邏輯:如果數組中第i個元素的值爲參數3,則將其替換成參數4
    return U.compareAndSwapObject(tab, ((long)i << ASHIFT) + ABASE, c, v);
}

static final <K,V> void setTabAt(Node<K,V>[] tab, int i, Node<K,V> v) {
    U.putObjectVolatile(tab, ((long)i << ASHIFT) + ABASE, v);
}
  • 增加:put(K key, V value)
public V put(K key, V value) {
    return putVal(key, value, false);
}

public V putIfAbsent(K key, V value) {
	return putVal(key, value, true);
}

/** Implementation for put and putIfAbsent */
final V putVal(K key, V value, boolean onlyIfAbsent) {
    if (key == null || value == null) throw new NullPointerException();
    int hash = spread(key.hashCode());
    int binCount = 0;
    for (Node<K,V>[] tab = table;;) {
        Node<K,V> f; int n, i, fh;
        if (tab == null || (n = tab.length) == 0)
            tab = initTable();
        else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) {
            if (casTabAt(tab, i, null,
                         new Node<K,V>(hash, key, value, null)))
                break;                   // no lock when adding to empty bin
        }
        else if ((fh = f.hash) == MOVED)
            tab = helpTransfer(tab, f);
        else {
            V oldVal = null;
            synchronized (f) {
                if (tabAt(tab, i) == f) {
                    if (fh >= 0) {
                        binCount = 1;
                        for (Node<K,V> e = f;; ++binCount) {
                            K ek;
                            if (e.hash == hash &&
                                ((ek = e.key) == key ||
                                 (ek != null && key.equals(ek)))) {
                                oldVal = e.val;
                                if (!onlyIfAbsent)
                                    e.val = value;
                                break;
                            }
                            Node<K,V> pred = e;
                            if ((e = e.next) == null) {
                                pred.next = new Node<K,V>(hash, key,
                                                          value, null);
                                break;
                            }
                        }
                    }
                    else if (f instanceof TreeBin) {
                        Node<K,V> p;
                        binCount = 2;
                        if ((p = ((TreeBin<K,V>)f).putTreeVal(hash, key,
                                                       value)) != null) {
                            oldVal = p.val;
                            if (!onlyIfAbsent)
                                p.val = value;
                        }
                    }
                }
            }
            if (binCount != 0) {
                if (binCount >= TREEIFY_THRESHOLD)
                    treeifyBin(tab, i);
                if (oldVal != null)
                    return oldVal;
                break;
            }
        }
    }
    addCount(1L, binCount);
    return null;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章