国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

HashMap源碼分析_JDK1.8版本

0xE7A38A / 476人閱讀

摘要:源碼分析版本聲明文章均為本人技術筆記,轉載請注明出處聲明結構圖示基本數據結構本質是一個散列表,存儲元素為鍵值對繼承,實現了接口的是線程不安全的,它的都可以為默認裝填因子,如果當前鍵值對個數最大容量裝填因子,進行操作新加,鏈表最大長度,當桶中

HashMap源碼分析_JDK1.8版本 聲明

文章均為本人技術筆記,轉載請注明出處
[1] https://segmentfault.com/u/yzwall
[2] blog.csdn.net/j_dark/

HashMap聲明

public class HashMap extends AbstractMap implements Map, Cloneable, Serializable

HashMap結構圖示

HashMap基本數據結構

HashMap本質是一個散列表,存儲元素為鍵值對;

HashMap繼承AbstractMap,實現了Map、Cloneable、java.io.Serializable接口;

HashMap的是線程不安全的,它的key、value都可以為null

final int loadFacotr

static final float DEFAULT_LOAD_FACTOR: 默認裝填因子0.75,如果當前鍵值對個數 >= HashMap最大容量*裝填因子,進行rehash操作;

int threshold

static final int TREEIFY_THRESHOLDJDK1.8 新加,Entry鏈表最大長度,當桶中節點數目大于該長度時,將鏈表轉成紅黑樹存儲;

static final int UNTREEIFY_THRESHOLDJDK1.8 新加,當桶中節點數小于該長度,將紅黑樹轉為鏈表存儲;

static final int DEFAULT_INITIAL_CAPACITY: 默認鍵值隊個數為16;

transient Node[] table:鍵值對數組,長度動態增加,但是總為2的冪;用transient修飾表示對象序列化時該字段不可被序列化;

HashMap鍵值對描述:Node

JDK1.6用Entry描述鍵值對,JDK1.8中用Node代替Entry;

static class Node implements Map.Entry {
    // hash存儲key的hashCode
    final int hash;
    // final:一個鍵值對的key不可改變
    final K key;
    V value;
    // 一個桶中的鍵值對用鏈表組織
    Node next;
    ...
}

HashMap中鍵值對的存儲形式為鏈表節點,hashCode相同的節點(位于同一個桶)用鏈表組織;

public final int hashCode() {
    return Objects.hashCode(key) ^ Objects.hashCode(value);
}

鍵值對元素hashCode = key的hashCode與value的hashCode,高16位與低16位按位異或運算;

紅黑樹:TreeNode

static final class TreeNode extends LinkedHashMap.Entry

JDK1.8新增,用來支持桶的紅黑樹結構實現

HashMap重要方法分析 HashMap添加/更新鍵值對:put/putVal方法

public V put(K key, V value)內部調用putVal方法實現;

public V put(K key, V value) {
    // 倒數第二個參數false:表示允許舊值替換
    // 最后一個參數true:表示HashMap不處于創建模式
    return putVal(hash(key), key, value, false, true);
}
putVal方法分析:
final V putVal(int hash, K key, V value, boolean onlyIfAbsent, boolean evict) {
    Node[] tab; Node p; int n, i;
    // 槽數組未初始化或者未擴容,先調用resize()擴容
    if ((tab = table) == null || (n = tab.length) == 0)
        n = (tab = resize()).length;
    /**
     * Hash函數,(n - 1) & hash 計算key將被放置的槽位;
     * (n - 1) & hash 本質上是hash % n,位運算更快
     */
    if ((p = tab[i = (n - 1) & hash]) == null)
        // 空桶,創建新的鍵值對節點,放入槽數組中;
        tab[i] = newNode(hash, key, value, null);
    // 鍵值對已在對應桶中
    else {
        Node e; K k;
        // 與桶中首元素比較,如果key不同發生Hash沖突,在桶中添加新元素
        if (p.hash == hash &&
            ((k = p.key) == key || (key != null && key.equals(k))))
            e = p;
        // 當前桶中無該鍵值對,且桶是紅黑樹結構,按照紅黑樹結構插入
        else if (p instanceof TreeNode)
            e = ((TreeNode)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);
                    // 檢查鏈表長度是否達到閾值,達到將該槽位節點組織形式轉為紅黑樹
                    if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
                        treeifyBin(tab, hash);
                    break;
                }
                // 鏈表節點的與put操作相同時,不做重復操作,跳出循環
                if (e.hash == hash &&
                    ((k = e.key) == key || (key != null && key.equals(k))))
                    break;
                p = e;
            }
        }
        // 找到 or 新建一個key和hashCode與插入元素相等的鍵值對,進行put操作
        if (e != null) { // existing mapping for key
            V oldValue = e.value;
            /**
             * onlyIfAbsent為false或舊值為null時,允許替換舊值
             * 否則無需替換
             */
            if (!onlyIfAbsent || oldValue == null)
                e.value = value;
            afterNodeAccess(e);
            return oldValue;
        }
    }
    // 更新結構化修改信息
    ++modCount;
    // 鍵值對數目超過閾值時,進行rehash
    if (++size > threshold)
        resize();
    afterNodeInsertion(evict);
    return null;
}
散列分布策略

鍵值對的槽位 = (容量 - 1) & hash(key)

鍵值對槽位是鍵值對在tab數組的索引,本質上 = hash(key) % 容量,位運算速度更快

本質上是除數取余法,盡可能的散列均勻;

Hash函數
// in HashMap
static final int hash(Object key) {
    int h;
    return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}

計算key的hashCode, h = Objects.hashCode(key);

h >>> 16表示對h無符號右移16位,高位補0;然后h與h >>> 16按位異或;

HashMap更新舊鍵值對 or 添加新鍵值對的核心思想

根據鍵值對key的hashCode計算鍵值對的在HashMap中槽位,

判斷是否空桶 Or 是否發生Hash沖突(與桶中首元素不同)

解決Hash沖突:根據桶組織形式是紅黑樹 Or 鏈表進行對應插入操作;

鏈表形式完成插入后,檢查是否超過鏈表閾值,超過將鏈表->紅黑樹;

最后檢查鍵值對總數是否超過閾值,超過調用resize()進行rehash操作;

HashMap刪除鍵值對:remove/removeNode方法 remove方法分析
public V remove(Object key) {
    Node e;
    return (e = removeNode(hash(key), key, null, false, true)) == null ?
        null : e.value;
}

remove()方法內部調用removeNode()方法實現

removeNode方法分析:
final Node removeNode(int hash, Object key, Object value,
                           boolean matchValue, boolean movable) {
    Node[] tab; Node p; int n, index;
    if ((tab = table) != null && (n = tab.length) > 0 &&
        (p = tab[index = (n - 1) & hash]) != null) {
        Node 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)p).getTreeNode(hash, key);
            else {
                // 遍歷鏈表,查找待刪除元素
                do {
                    if (e.hash == hash &&
                        ((k = e.key) == key ||
                         (key != null && key.equals(k)))) {
                        node = e;
                        break;
                    }
                    // p保存待刪除節點的前一個節點,用于鏈表刪除操作
                    p = e;
                } while ((e = e.next) != null);
            }
        }
        /**
         * matchValue為true:表示必須value相等才進行刪除操作
         * matchValue為false:表示無須判斷value,直接根據key進行刪除操作
         */
        if (node != null && (!matchValue || (v = node.value) == value ||
                             (value != null && value.equals(v)))) {
            // 桶為紅黑數結構,刪除節點
            if (node instanceof TreeNode)
                // movable參數用于紅黑樹操作
                ((TreeNode)node).removeTreeNode(this, tab, movable);
            // 待刪除節點是桶鏈表表頭,將子節點放進桶位
            else if (node == p)
                tab[index] = node.next;
            // 待刪除節點在桶鏈表中間
            else
                p.next = node.next;
            ++modCount;
            --size;
            afterNodeRemoval(node);
            return node;
        }
    }
    // 待刪除元素不存在,返回null
    return null;
}
HashMap訪問鍵值對:get/getNode方法 get方法
public V get(Object key) {
    Node e;
    return (e = getNode(hash(key), key)) == null ? null : e.value;
}

get方法通過指定key獲得對應鍵值對,內部調用getNode方法實現;

getNode方法
final Node getNode(int hash, Object key) {
    Node[] tab; Node 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)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;
}

getNode方法查找過程和putVal一樣,先查找對應桶的首元素,然后根據紅黑樹結構 Or 鏈表結構對應查找;

HashMap重散列操作:resize方法
final Node[] resize() {
    // 保存舊table,容量,閾值
    Node[] oldTab = table;
    int oldCap = (oldTab == null) ? 0 : oldTab.length;
    int oldThr = threshold;
    int newCap, newThr = 0;
    if (oldCap > 0) {
        // 舊table容量已超過最大容量,更新閾值為Integer.MAX_VALUE
        if (oldCap >= MAXIMUM_CAPACITY) {
            threshold = Integer.MAX_VALUE;
            return oldTab;
        }
        // 調整新容量為舊容量2倍,左移一位實現
        else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
                 oldCap >= DEFAULT_INITIAL_CAPACITY)
            newThr = oldThr << 1; // double threshold
    }
    // oldCap == 0 && oldThr > 0
    else if (oldThr > 0) // initial capacity was placed in threshold
        newCap = oldThr;
    // oldCap == 0 && oldThr == 0
    else {               // zero initial threshold signifies using defaults
        newCap = DEFAULT_INITIAL_CAPACITY;
        newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
    }
    if (newThr == 0) {
        float ft = (float)newCap * loadFactor;
        newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ?
                  (int)ft : Integer.MAX_VALUE);
    }
    threshold = newThr;
    @SuppressWarnings({"rawtypes","unchecked"})
        Node[] newTab = (Node[])new Node[newCap];
    table = newTab;
    if (oldTab != null) {
        for (int j = 0; j < oldCap; ++j) {
            Node e;
            if ((e = oldTab[j]) != null) {
                oldTab[j] = null;
                if (e.next == null)
                    newTab[e.hash & (newCap - 1)] = e;
                // 紅黑樹桶進行rehash操作
                else if (e instanceof TreeNode)
                    ((TreeNode)e).split(this, newTab, j, oldCap);
                
                // 鏈表桶進行rehash操作
                // 根據e.hash & oldCap)是否為0把鏈表分成兩個鏈表,進行rehash
                else { // preserve order
                    Node loHead = null, loTail = null;
                    Node hiHead = null, hiTail = null;
                    Node next;
                    do {
                        next = e.next;
                        if ((e.hash & oldCap) == 0) {
                            if (loTail == null)
                                loHead = e;
                            else
                                loTail.next = e;
                            loTail = e;
                        }
                        else {
                            if (hiTail == null)
                                hiHead = e;
                            else
                                hiTail.next = e;
                            hiTail = e;
                        }
                    } while ((e = next) != null);
                    if (loTail != null) {
                        loTail.next = null;
                        newTab[j] = loHead;
                    }
                    if (hiTail != null) {
                        hiTail.next = null;
                        newTab[j + oldCap] = hiHead;
                    }
                }
            }
        }
    }
    return newTab;
}

當鍵值對總數超過閾值threshold, HashMap通過resize方法實現重散列/rehash

HashMap調整容量:tableSizeFor()

static final int tableSizeFor(int cap):得到>=cap的2的最小冪值;
由指定容量參數的構造器調用,計算rehash閾值threshold;

參考

[1] http://www.cnblogs.com/leesf456/p/5242233.html
[2] http://www.cnblogs.com/ToBeAProgrammer

文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。

轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/66967.html

相關文章

  • 集合框架源碼學習之HashMap(JDK1.8)

    摘要:所謂拉鏈法就是將鏈表和數組相結合。若遇到哈希沖突,則將沖突的值加到鏈表中即可。在編寫程序中,要盡量避免。 目錄: 0-1. 簡介 0-2. 內部結構分析   0-2-1. JDK18之前   0-2-2. JDK18之后 0-3. LinkedList源碼分析   0-3-1. 構造方法   0-3-2. put方法   0-3-3. get方法   0-3-4. resize方法 ...

    yangrd 評論0 收藏0
  • HashMap 源碼詳細分析(JDK1.8)

    摘要:則使用了拉鏈式的散列算法,并在中引入了紅黑樹優化過長的鏈表。如果大家對紅黑樹感興趣,可以閱讀我的另一篇文章紅黑樹詳細分析。構造方法構造方法分析的構造方法不多,只有四個。 1.概述 本篇文章我們來聊聊大家日常開發中常用的一個集合類 - HashMap。HashMap 最早出現在 JDK 1.2中,底層基于散列算法實現。HashMap 允許 null 鍵和 null 值,在計算哈鍵的哈希值...

    monw3c 評論0 收藏0
  • 前百度面試官整理的——Java后端面試題(一)

    摘要:發生了線程不安全情況。本來在中,發生哈希沖突是可以用鏈表法或者紅黑樹來解決的,但是在多線程中,可能就直接給覆蓋了。中,當同一個值上元素的鏈表節點數不小于時,將不再以單鏈表的形式存儲了,會被調整成一顆紅黑樹。 showImg(https://segmentfault.com/img/bVbsVLk?w=288&h=226); List 和 Set 的區別 List , Set 都是繼承自...

    JessYanCoding 評論0 收藏0
  • HashMap源碼分析(一):JDK源碼分析系列

    摘要:當一個值中要存儲到的時候會根據的值來計算出他的,通過哈希來確認到數組的位置,如果發生哈希碰撞就以鏈表的形式存儲在源碼分析中解釋過,但是這樣如果鏈表過長來的話,會把這個鏈表轉換成紅黑樹來存儲。 正文開始 注:JDK版本為1.8 HashMap1.8和1.8之前的源碼差別很大 目錄 簡介 數據結構 類結構 屬性 構造方法 增加 刪除 修改 總結 1.HashMap簡介 H...

    wdzgege 評論0 收藏0
  • ConcurrentHashMap源碼分析_JDK1.8版本

    ConcurrentHashMap源碼分析_JDK1.8版本 聲明 文章均為本人技術筆記,轉載請注明出處[1] https://segmentfault.com/u/yzwall[2] blog.csdn.net/j_dark/ JDK1.6版本 ConcurrentHashMap結構 showImg(https://segmentfault.com/img/remote/146000000900...

    animabear 評論0 收藏0

發表評論

0條評論

0xE7A38A

|高級講師

TA的文章

閱讀更多
最新活動
閱讀需要支付1元查看
<