欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Java中的HashMap源碼解析

 更新時(shí)間:2023年12月19日 10:35:32   作者:會(huì)飛的豬zhu  
這篇文章主要介紹了Java中的HashMap源碼解析,當(dāng)HashMap中的其中一個(gè)鏈表的對象個(gè)數(shù)如果達(dá)到了8個(gè),此時(shí)如果數(shù)組長度沒有達(dá)到64,那么HashMap會(huì)先擴(kuò)容解決,如果已經(jīng)達(dá)到了64,那么這個(gè)鏈表會(huì)變成紅黑樹,需要的朋友可以參考下

HashMap

靜態(tài)常量

   //序列化版本號,在序列化和反序列化的時(shí)候使用
   private static final long serialVersionUID = 362498820763181265L;
  //集合初始容量為16,   
  static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
   //最大容量 table數(shù)組存放元素的最大數(shù)量  
   static final int MAXIMUM_CAPACITY = 1 << 30;
    //負(fù)載因子默認(rèn)為0.75   負(fù)載因子= 表中的元素個(gè)數(shù)/散列表的長度,達(dá)到0.75擴(kuò)容  
    static final float DEFAULT_LOAD_FACTOR = 0.75f;
    //鏈表轉(zhuǎn)成樹的閾值,鏈表中的元素個(gè)數(shù)大于8,變?yōu)榧t黑樹和MIN_TREEIFY_CAPACITY一起決定的
    static final int TREEIFY_THRESHOLD = 8;
     //樹轉(zhuǎn)換成鏈表的閾值    樹中的元素小于6,轉(zhuǎn)換為鏈表
     static final int UNTREEIFY_THRESHOLD = 6;
    //最小轉(zhuǎn)成樹的容量,當(dāng)數(shù)組長度達(dá)到64轉(zhuǎn)換為紅黑樹----和TREEIFY_THRESHOLD一起決定的
    static final int MIN_TREEIFY_CAPACITY = 64;

成員變量 

   //存放元素的數(shù)組 
    transient Node<K,V>[] table;
    //存放Entry類型的元素集合  
    transient Set<Map.Entry<K,V>> entrySet;
   //哈希表中元素的個(gè)數(shù)
    transient int size;
    //每次擴(kuò)容和更改map結(jié)構(gòu)的計(jì)數(shù)器
    transient int modCount;
   //擴(kuò)容的閾值,當(dāng)實(shí)際大小為(16 * 0.75 = 12) 閾值時(shí)開始擴(kuò)容 
    int threshold;
   //哈希表自己的負(fù)載因子
    final float loadFactor;

構(gòu)造函數(shù)

//指定容量大小的構(gòu)造函數(shù)
public HashMap(int initialCapacity) {
    this(initialCapacity, DEFAULT_LOAD_FACTOR);
}
/*
	 指定“容量大小”和“加載因子”的構(gòu)造函數(shù)
	 initialCapacity: 指定的容量
	 loadFactor:指定的加載因子
*/
public HashMap(int initialCapacity, float loadFactor) {
    //判斷初始化容量initialCapacity是否小于0
    if (initialCapacity < 0)
        //如果小于0,則拋出非法的參數(shù)異常IllegalArgumentException
        throw new IllegalArgumentException("Illegal initial capacity: " + initialCapacity);
    //判斷初始化容量initialCapacity是否大于集合的最大容量MAXIMUM_CAPACITY-》2的30次冪
    if (initialCapacity > MAXIMUM_CAPACITY)
        //如果超過MAXIMUM_CAPACITY,會(huì)將MAXIMUM_CAPACITY賦值給initialCapacity
        initialCapacity = MAXIMUM_CAPACITY;
    //判斷負(fù)載因子loadFactor是否小于等于0或者是否是一個(gè)非數(shù)值
    if (loadFactor <= 0 || Float.isNaN(loadFactor))
        //如果滿足上述其中之一,則拋出非法的參數(shù)異常IllegalArgumentException
        throw new IllegalArgumentException("Illegal load factor: " + loadFactor);
    //將指定的加載因子賦值給HashMap成員變量的負(fù)載因子loadFactor
    this.loadFactor = loadFactor;
    /*
    		tableSizeFor(initialCapacity) 判斷指定的初始化容量是否是2的n次冪,如果不是那么會(huì)變?yōu)楸戎付ǔ跏蓟萘看蟮淖钚〉?的n次冪。
    		但是注意,在tableSizeFor方法體內(nèi)部將計(jì)算后的數(shù)據(jù)返回給調(diào)用這里了,并且直接賦值給threshold邊界值了。有些人會(huì)覺得這里是一個(gè)bug,應(yīng)該這樣書寫:
    		this.threshold = tableSizeFor(initialCapacity) *this.loadFactor;
    		這樣才符合threshold的意思(當(dāng)HashMap的size到達(dá)threshold這個(gè)閾值時(shí)會(huì)擴(kuò)容)。
			但是,請注意,在jdk8以后的構(gòu)造方法中,并沒有對table這個(gè)成員變量進(jìn)行初始化,table的初始化被推遲到了put方法中,在put方法中會(huì)對threshold重新計(jì)算
    	*/
    this.threshold = tableSizeFor(initialCapacity);
}
//無參構(gòu)造
 public HashMap() {
        this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted
    }
//返回比指定初始容量大的最小的2的n次方
  static final int tableSizeFor(int cap) {
        int n = cap - 1;
        n |= n >>> 1;
        n |= n >>> 2;
        n |= n >>> 4;
        n |= n >>> 8;
        n |= n >>> 16;
        return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
    }

put()方法流程 

public V put(K key, V value) {
        return putVal(hash(key), key, value, false, true);
}
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,boolean evict) {
    //transient Node<K,V>[] table; 表示存儲(chǔ)Map集合中元素的數(shù)組。
    Node<K,V>[] tab;
    Node<K,V> p; 
    //n代表數(shù)組的長度
    int n, i;
    // ① 將table賦值給tab,并判斷是否為null,第一次賦值時(shí)肯定為null
    // ② 將tab.length賦值給n,并判斷是否為0
    if ((tab = table) == null || (n = tab.length) == 0)
        //③ 由于數(shù)組為tab==null,對數(shù)組進(jìn)行初始化,并將初始化后的數(shù)組長度賦值給n
        //④ 執(zhí)行n = (tab = resize()).length,數(shù)組tab每個(gè)空間都是null
        n = (tab = resize()).length;
    // ① i = (n - 1) & hash 表示計(jì)算數(shù)組的索引賦值給i,即確定元素存放在哪個(gè)桶中
    // ② p = tab[i = (n - 1) & hash] 將計(jì)算出的數(shù)組索引對應(yīng)的數(shù)據(jù)賦值給節(jié)點(diǎn)p
    // ③ 判斷p是否為null,即當(dāng)前桶沒有哈希沖突,則直接把鍵值對插入空間位置
    if ((p = tab[i = (n - 1) & hash]) == null)
        // ④ 根據(jù)鍵值對創(chuàng)建新的節(jié)點(diǎn)放入該位置的桶中
        // p表示tab[i],即 newNode(hash, key, value, null)方法返回的Node對象。
        tab[i] = newNode(hash, key, value, null);
    else {
        // ① 執(zhí)行else說明p=tab[i]不等于null,表示這個(gè)位置已經(jīng)有值了,
        Node<K,V> e; K k;
        // ② p.hash == hash:比較桶中元素的hash值和新添加元素key的hash值是否相等
        // ③ (k = p.key) == key:比較兩個(gè)key的地址值是否相等
        // ④ (key != null && key.equals(k)):能夠執(zhí)行到這里說明兩個(gè)key的地址值不相等,
 //那么先判斷后添加的key是否等于null,如果不等于null再調(diào)用equals方法判斷兩個(gè)key的內(nèi)容是否相等
        if (p.hash == hash && ((k = p.key) == key || (key != null && key.equals(k))))
            	// 兩個(gè)元素哈希值相等,并且key的值也相等,將舊的元素整體對象賦值給e,用e來記錄
                e = p;
        // hash值不相等或者key不相等;判斷p是否為紅黑樹結(jié)點(diǎn)
        else if (p instanceof TreeNode)
            // 放入樹中
            e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
        // hash值不相等或者key不相等,說明是鏈表節(jié)點(diǎn)
        else {
            // 如果hash(key)不相同,說明是計(jì)算的桶下標(biāo)相同,key也不相同,說明直接插在鏈表最后
            for (int binCount = 0; ; ++binCount) {
                // ① e = p.next 獲取p的下一個(gè)元素賦值給e
                // ② 判斷p.next是否等于null,等于null,說明p沒有下一個(gè)元素
                // ③ 到達(dá)了鏈表的尾部,還沒有找到重復(fù)的key,說明HashMap沒有包含該鍵,將該鍵值對插入鏈表中
                if ((e = p.next) == null) {
                    //④ 創(chuàng)建一個(gè)新的節(jié)點(diǎn)插入到尾部
                    p.next = newNode(hash, key, value, null);
                    //① 節(jié)點(diǎn)添加完成之后判斷此時(shí)節(jié)點(diǎn)個(gè)數(shù)是否大于臨界值8,如果大于則將鏈表轉(zhuǎn)換為紅黑樹
                    //② binCount從0開始計(jì)數(shù),TREEIFY_THRESHOLD - 1=7
                    if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
                        //轉(zhuǎn)換為紅黑樹
                        treeifyBin(tab, hash);
                    break;
                }
                // ① e = p.next 不是null,不是最后一個(gè)元素。
                // ②繼續(xù)判斷鏈表中結(jié)點(diǎn)的key值與插入的元素的key值是否相等
                if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k))))
                    // 相等,跳出循環(huán)
                    //要添加的元素和鏈表中的存在的元素的key相等了,則跳出for循環(huán)。
                    //不用再繼續(xù)比較了,直接執(zhí)行下面的if語句去替換去 if (e != null) 
                    break;
                //說明新添加的元素和當(dāng)前節(jié)點(diǎn)不相等,繼續(xù)查找下一個(gè)節(jié)點(diǎn)。
                // 用于遍歷桶中的鏈表,與前面的e = p.next組合,可以遍歷鏈表
                p = e;
            }
        }
        /*
        	表示在桶中找到key值、hash值與插入元素相等的結(jié)點(diǎn)
        	也就是說通過上面的操作找到了重復(fù)的鍵,所以這里就是把該鍵的值變?yōu)樾碌闹担⒎祷嘏f值
        	這里完成了put方法的修改功能
        */
        if (e != null) { 
            // 記錄e的value
            V oldValue = e.value;
            // onlyIfAbsent為false或者舊值為null
            if (!onlyIfAbsent || oldValue == null)
                //用新值替換舊值:e.value 表示舊值  value表示新值 
                e.value = value;
            // 訪問后回調(diào)
            afterNodeAccess(e);
            // 返回舊值
            return oldValue;
        }
    }
    //修改記錄次數(shù)
    ++modCount;
    // 判斷實(shí)際大小是否大于threshold閾值,如果超過則擴(kuò)容
    if (++size > threshold)
        resize();
    // 插入后回調(diào)
    afterNodeInsertion(evict);
    return null;
} 

hash()方法實(shí)現(xiàn) 

① 計(jì)算hash(key)

對于key的hashCode做hash操作,無符號右移16位后做異或運(yùn)算。 還有偽隨機(jī)數(shù)法和取余數(shù)法。這2種效率都比較低。而無符號右移16位和異或運(yùn)算效率是最高的。

static final int hash(Object key) {
        int h;
        return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
    }

② 計(jì)算數(shù)組桶下標(biāo): hash(key) & (table.length-1) 

final V putVal(int hash, K key, V value, boolean onlyIfAbsent,boolean evict) {
    	// n表示數(shù)組初始化的長度是16 
    	// 數(shù)組的長度必須為2的n次方,這樣能夠讓hash(key)更加均勻的分布在桶下標(biāo)中,減少hash沖突
    	// 使用 & 運(yùn)算是為了提高效率
        if ((p = tab[i = (n - 1) & hash]) == null)
}

具體流程:

resize方法的實(shí)現(xiàn) 

當(dāng)HashMap中的元素個(gè)數(shù)超過數(shù)組大小 *loadFactor時(shí),就會(huì)進(jìn)行數(shù)組擴(kuò)容,loadFactor的默認(rèn)值是0.75,這是一個(gè)折中的取值。也就是說,默認(rèn)情況下,數(shù)組大小為16,那么當(dāng)HashMap中的元素個(gè)數(shù)超過16×0.75=12(這個(gè)值就是閾值或者邊界值threshold值)的時(shí)候,就把數(shù)組的大小擴(kuò)展為2×16=32,即擴(kuò)大一倍,然后重新計(jì)算每個(gè)元素在數(shù)組中的位置,而這是一個(gè)非常耗性能的操作,所以如果我們已經(jīng)預(yù)知HashMap中元素的個(gè)數(shù),那么預(yù)知元素的個(gè)數(shù)能夠有效的提高HashMap的性能。

當(dāng)HashMap中的其中一個(gè)鏈表的對象個(gè)數(shù)如果達(dá)到了8個(gè),此時(shí)如果數(shù)組長度沒有達(dá)到64,那么HashMap會(huì)先擴(kuò)容解決,如果已經(jīng)達(dá)到了64,那么這個(gè)鏈表會(huì)變成紅黑樹,節(jié)點(diǎn)類型由Node變成TreeNode類型。當(dāng)然,如果映射關(guān)系被移除后,下次執(zhí)行resize方法時(shí)判斷樹的節(jié)點(diǎn)個(gè)數(shù)低于6,也會(huì)再把樹轉(zhuǎn)換為鏈表。

進(jìn)行擴(kuò)容,會(huì)伴隨著一次重新hash分配,并且會(huì)遍歷hash表中所有的元素,是非常耗時(shí)的。在編寫程序中,要盡量避免resize。

HashMap在進(jìn)行擴(kuò)容時(shí),使用的rehash方式非常巧妙,因?yàn)槊看螖U(kuò)容都是翻倍,與原來計(jì)算的 (n-1)&hash的結(jié)果相比,只是多了一個(gè)bit位,所以節(jié)點(diǎn)要么就在原來的位置,要么就被分配到"原位置+舊容量"這個(gè)位置。

怎么理解呢?例如我們從16擴(kuò)展為32時(shí),具體的變化如下所示: 

元素在重新計(jì)算hash之后,因?yàn)閚變?yōu)?倍,那么n-1的標(biāo)記范圍在高位多1bit(紅色),因此新的index就會(huì)發(fā)生這樣的變化:  

說明:5是假設(shè)計(jì)算出來的原來的索引。這樣就驗(yàn)證了上述所描述的:擴(kuò)容之后所以節(jié)點(diǎn)要么就在原來的位置,要么就被分配到"原位置+舊容量"這個(gè)位置。如果新的索引高位為0那么存儲(chǔ)在原來索引位置,如果高位是1那么存在原來索引+舊的數(shù)組長度位置。

因此,我們在擴(kuò)充HashMap的時(shí)候,不需要重新計(jì)算hash,只需要看看原來的hash值新增的那個(gè)bit是1還是0就可以了,是0的話索引沒變,是1的話索引變成“原索引+oldCap(原位置+舊容量)”??梢钥纯聪聢D為16擴(kuò)充為32的resize示意圖:  

正是因?yàn)檫@樣巧妙的rehash方式,既省去了重新計(jì)算hash值的時(shí)間,而且同時(shí),由于新增的1bit是0還是1可以認(rèn)為是隨機(jī)的,在resize的過程中保證了rehash之后每個(gè)桶上的節(jié)點(diǎn)數(shù)一定小于等于原來桶上的節(jié)點(diǎn)數(shù),保證了rehash之后不會(huì)出現(xiàn)更嚴(yán)重的hash沖突,均勻的把之前的沖突的節(jié)點(diǎn)分散到新的桶中了。

final Node<K,V>[] resize() {
    //得到當(dāng)前數(shù)組
    Node<K,V>[] oldTab = table;
    //如果當(dāng)前數(shù)組等于null長度返回0,否則返回當(dāng)前數(shù)組的長度
    int oldCap = (oldTab == null) ? 0 : oldTab.length;
    //舊閾值點(diǎn) 默認(rèn)是12(16*0.75)
    int oldThr = threshold;
    int newCap, newThr = 0;
   //第一次的數(shù)組容量都是0肯定
    // ① 數(shù)組容量>0
    if (oldCap > 0) {
        // ① 超過最大值就不再擴(kuò)充了,就只好隨你碰撞去吧
        if (oldCap >= MAXIMUM_CAPACITY) {
            threshold = Integer.MAX_VALUE;
            return oldTab;
        }
        // ② 沒超過最大值,數(shù)組容量就擴(kuò)充為原來的2倍,newCap = 16*2=32
        else if((newCap = oldCap << 1)< MAXIMUM_CAPACITY && oldCap >= DEFAULT_INITIAL_CAPACITY)
            //閾值擴(kuò)大一倍, 默認(rèn)原來是12 乘以2之后變?yōu)?4
            newThr = oldThr << 1; 
    }
    //② 舊閾值點(diǎn)大于0 直接賦值
    else if (oldThr > 0) 
        newCap = oldThr;
    //③ 直接使用默認(rèn)值
    else {
        newCap = DEFAULT_INITIAL_CAPACITY;//16
        newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
    }
    // ① 如果新的容量為0
    if (newThr == 0) {
        float ft = (float)newCap * loadFactor;
        newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ?
                  (int)ft : Integer.MAX_VALUE);
    }
    // ② 新的閾值threshold = 24
    threshold = newThr;
    // ③ 創(chuàng)建新的哈希表,數(shù)組大小為newCap=32
    Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];
    table = newTab;
    // ④ 判斷舊數(shù)組不等于空,遍歷舊的哈希表 ,重新計(jì)算桶里元素的新位置,把每個(gè)bucket都移動(dòng)到新的buckets中
    if (oldTab != null) {
        // 把每個(gè)bucket都移動(dòng)到新的buckets中
        for (int j = 0; j < oldCap; ++j) {
            Node<K,V> e;
            //取出桶中的元素賦值給e
            if ((e = oldTab[j]) != null) {
                oldTab[j] = null;
                //① 如果沒有下一個(gè)節(jié)點(diǎn),說明不是鏈表,當(dāng)前桶上只有一個(gè)鍵值對,直接插入
                if (e.next == null)
                    newTab[e.hash & (newCap - 1)] = e;
                //② 判斷是否是紅黑樹節(jié)點(diǎn)
                else if (e instanceof TreeNode)
                    //說明是紅黑樹來處理沖突的,則調(diào)用相關(guān)方法把樹分開
                    ((TreeNode<K,V>)e).split(this, newTab, j, oldCap);
                //③ 說明是鏈表節(jié)點(diǎn)
                else {
                    Node<K,V> loHead = null, loTail = null;
                    Node<K,V> hiHead = null, hiTail = null;
                    Node<K,V> next;
                    //遍歷鏈表計(jì)算節(jié)點(diǎn)要插入新數(shù)組的位置
                    do {
                        //原索引
                        next = e.next;
                     	//① e這個(gè)節(jié)點(diǎn)在resize之后不需要移動(dòng)位置 
                        if ((e.hash & oldCap) == 0) {
                            if (loTail == null)
                                loHead = e;
                            else
                                loTail.next = e;
                            loTail = e;
                        }
                        //② 否則e這個(gè)節(jié)點(diǎn)要插入的位置為:原索引+oldCap
                        else {
                            if (hiTail == null)
                                hiHead = e;
                            else
                                hiTail.next = e;
                            hiTail = e;
                        }
                    } while ((e = next) != null);
                    // 原索引放到bucket里
                    if (loTail != null) {
                        loTail.next = null;
                        newTab[j] = loHead;
                    }
                    // 原索引+oldCap放到bucket里
                    if (hiTail != null) {
                        hiTail.next = null;
                        newTab[j + oldCap] = hiHead;
                    }
                }
            }
        }
    }
    return newTab;
}

總結(jié):

1.如果當(dāng)前數(shù)組的容量大于0并且小于數(shù)組容量的最大值,那么就會(huì)將數(shù)組容量擴(kuò)容為原來的2倍

2. 如果數(shù)組容量為0,但是閾值大于0,那么table會(huì)初始化為閾值大小

3. 如果數(shù)組容量為0,且閾值為0,那么table會(huì)初始化為默認(rèn)值16

4. 創(chuàng)建一個(gè)新的數(shù)組,數(shù)組的容量為擴(kuò)容后的數(shù)組大小,然后遍歷原來數(shù)組中的元素:

5.如果數(shù)組桶下標(biāo)處只有一個(gè)鍵值對,將當(dāng)前位置的元素直接插入到新數(shù)組對應(yīng)桶下標(biāo)處

6.如果紅黑樹節(jié)點(diǎn),拆分紅黑樹解決哈希沖突

7.如果是鏈表節(jié)點(diǎn),遍歷鏈表,并計(jì)算鏈表中每個(gè)節(jié)點(diǎn)應(yīng)該插入到新數(shù)組中的位置

8.如果當(dāng)前節(jié)點(diǎn)的hash值和原來數(shù)組的容量進(jìn)行與運(yùn)算等于0,那么當(dāng)前元素插入新數(shù)組的該索引處

9.如果當(dāng)前節(jié)點(diǎn)的hash值和原來數(shù)組的容量進(jìn)行與運(yùn)算等于1,那么當(dāng)前元素插入到新數(shù)組的位置為:當(dāng)前元素在舊數(shù)組中的索引+舊數(shù)組的長度  

treeifyBin()方法實(shí)現(xiàn) 

final void treeifyBin(Node<K,V>[] tab, int hash) {
    int n, index; Node<K,V> e;
    //① 如果數(shù)組為空,或者數(shù)組的長度小于64,就先擴(kuò)容,而不是將節(jié)點(diǎn)轉(zhuǎn)為紅黑樹
    if (tab == null || (n = tab.length) < MIN_TREEIFY_CAPACITY)
        resize();
    //②  執(zhí)行到這里說明哈希表中的數(shù)組長度大于閾值64,開始進(jìn)行樹形化
    // 將數(shù)組中的元素取出賦值給e,e是哈希表中指定位置桶里的鏈表節(jié)點(diǎn),從第一個(gè)開始
    else if ((e = tab[index = (n - 1) & hash]) != null) {
        ///hd:紅黑樹的頭結(jié)點(diǎn)   tl :紅黑樹的尾結(jié)點(diǎn)
        TreeNode<K,V> hd = null, tl = null;
        do {
            //新創(chuàng)建一個(gè)樹的節(jié)點(diǎn),內(nèi)容和當(dāng)前鏈表節(jié)點(diǎn)e一致
            TreeNode<K,V> p = replacementTreeNode(e, null);
            //將新創(chuàng)鍵的p節(jié)點(diǎn)賦值給紅黑樹的頭結(jié)點(diǎn)
            if (tl == null)
                hd = p;
            else {
                p.prev = tl;
                tl.next = p;
            }
            tl = p;
        } while ((e = e.next) != null);
        //讓桶中的第一個(gè)元素即數(shù)組中的元素指向新建的紅黑樹的節(jié)點(diǎn),以后這個(gè)桶里的元素就是紅黑樹,而不是鏈表數(shù)據(jù)結(jié)構(gòu)了
        if ((tab[index] = hd) != null)
            hd.treeify(tab);
    }
}

 get()方法流程 

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) {
        //判斷數(shù)組中的第一個(gè)節(jié)點(diǎn)的hash、key的地址、key的內(nèi)容是否和要查找的key相同,如果相同返回即可
        if (first.hash == hash && ((k = first.key) == key || (key != null && key.equals(k))))
            return first;
        //如果數(shù)組中節(jié)點(diǎn)還指向了下一個(gè)節(jié)點(diǎn)
        if ((e = first.next) != null) {
            //如果是紅黑樹,調(diào)用紅黑樹的getTreeNode()方法
            if (first instanceof TreeNode)
                return ((TreeNode<K,V>)first).getTreeNode(hash, key);
            //如果是鏈表,遍歷鏈表,查找與key相同的鍵對應(yīng)的節(jié)點(diǎn),找到只有返回當(dāng)前節(jié)點(diǎn)
            do {
                if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k))))
                    return e;
            } while ((e = e.next) != null);
        }
    }
    return null;
}

到此這篇關(guān)于Java中的HashMap源碼解析的文章就介紹到這了,更多相關(guān)HashMap源碼內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • java實(shí)現(xiàn)日歷(某年的日歷,某月的日歷)用戶完全自定義

    java實(shí)現(xiàn)日歷(某年的日歷,某月的日歷)用戶完全自定義

    本篇文章介紹了,java實(shí)現(xiàn)日歷(某年的日歷,某月的日歷)用戶完全自定義。需要的朋友參考下
    2013-05-05
  • Spring MVC傳遞接收參數(shù)方式小結(jié)

    Spring MVC傳遞接收參數(shù)方式小結(jié)

    大家在開發(fā)中經(jīng)常會(huì)用到Spring MVC Controller來接收請求參數(shù),主要常用的接收方式就是通過實(shí)體對象以及形參等方式、有些用于GET請求,有些用于POST請求,有些用于兩者,下面介紹幾種常見的Spring MVC傳遞接收參數(shù)的方式
    2021-11-11
  • spring boot實(shí)戰(zhàn)之本地jar包引用示例

    spring boot實(shí)戰(zhàn)之本地jar包引用示例

    本篇文章主要介紹了spring boot實(shí)戰(zhàn)之本地jar包引用示例,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2017-10-10
  • IntelliJ IDEA窗口組件具體操作方法

    IntelliJ IDEA窗口組件具體操作方法

    IDEA剛接觸不久,各種常用工具窗口找不到,不小心關(guān)掉不知道從哪里打開,今天小編給大家分享這個(gè)問題的解決方法,感興趣的朋友一起看看吧
    2021-09-09
  • 基于Java Callable接口實(shí)現(xiàn)線程代碼實(shí)例

    基于Java Callable接口實(shí)現(xiàn)線程代碼實(shí)例

    這篇文章主要介紹了基于Java Callable接口實(shí)現(xiàn)線程代碼實(shí)例,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-08-08
  • springboot?實(shí)現(xiàn)不同context-path下的會(huì)話共享

    springboot?實(shí)現(xiàn)不同context-path下的會(huì)話共享

    這篇文章主要介紹了springboot?實(shí)現(xiàn)不同context-path下的會(huì)話共享,基于很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-01-01
  • Lambda表達(dá)式的使用及注意事項(xiàng)

    Lambda表達(dá)式的使用及注意事項(xiàng)

    這篇文章主要介紹了Lambda表達(dá)式的使用及注意事項(xiàng),主要圍繞?Lambda表達(dá)式的省略模式?Lambda表達(dá)式和匿名內(nèi)部類的區(qū)別的相關(guān)內(nèi)容展開詳情,感興趣的小伙伴可以參考一下
    2022-06-06
  • Java線程休眠之sleep方法詳解

    Java線程休眠之sleep方法詳解

    這篇文章主要介紹了Java線程休眠之sleep方法詳解,Thread?類中有一個(gè)靜態(tài)方法的sleep方法,當(dāng)該線程調(diào)用sleep方法后,就會(huì)暫時(shí)讓CPU的調(diào)度權(quán),但是監(jiān)視器資源比如鎖并不會(huì)釋放出去,需要的朋友可以參考下
    2024-01-01
  • Java多線程中Callable和Future的解讀

    Java多線程中Callable和Future的解讀

    這篇文章主要介紹了Java多線程中Callable和Future的解讀,Callable接口類似于Runnable,從名字就可以看出來了,但是Runnable不會(huì)返回結(jié)果,并且無法拋出返回結(jié)果的異常,而Callable功能更強(qiáng)大一些,被線程執(zhí)行后,可以返回值,這個(gè)返回值可以被Future拿到,需要的朋友可以參考下
    2023-09-09
  • java多線程編程之InheritableThreadLocal

    java多線程編程之InheritableThreadLocal

    這篇文章主要為大家詳細(xì)介紹了java多線程編程之InheritableThreadLocal,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-10-10

最新評論