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

Java中的HashMap源碼分析

 更新時間:2023年09月26日 08:50:24   作者:你好世界wxx  
這篇文章主要介紹了Java中的HashMap源碼分析,散列表是根據(jù)關(guān)鍵碼值(Key?value)而直接進行訪問的數(shù)據(jù)結(jié)構(gòu),也就是說,它通過把關(guān)鍵碼值映射到表中一個位置來訪問記錄,以加快查找的速度,這個映射函數(shù)叫做散列函數(shù),存放記錄的數(shù)組叫做散列表,需要的朋友可以參考下

1.HashMap源碼分析

  • 散列表(也叫哈希表),是根據(jù)關(guān)鍵碼值(Key value)而直接進行訪問的數(shù)據(jù)結(jié)構(gòu)。也就是說,它通過把關(guān)鍵碼值映射到表中一個位置來訪問記錄,以加快查找的速度。這個映射函數(shù)叫做散列函數(shù),存放記錄的數(shù)組叫做散列表。
  • 不同語言中都有哈希表的實現(xiàn),在C++中unordered_map是哈希表的具體實現(xiàn),在Java中HashMap是哈希表的具體實現(xiàn)。unordered_map和HashMap基本上都能做到O(1)時間內(nèi)的增刪改查操作,時間性能十分優(yōu)秀。
  • 我們都知道這個世界上沒有免費的午餐,既然unordered_map和HashMap時間性能如此優(yōu)秀,那么其他方面一定有所不足。事實正是如此,unordered_map和HashMap在操作的同時無法保證數(shù)據(jù)的有序性,即犧牲了數(shù)據(jù)的有序性,換取了優(yōu)秀的時間性能。
  • 那么有沒有既可以在O(1)的時間內(nèi)完成增刪改查操作,又可以保證數(shù)據(jù)的有序性的數(shù)據(jù)結(jié)構(gòu)呢?據(jù)我所知,沒有(可能有,但我不知道)。不過C++中的map和Java中的TreeMap可以在操作的同時保證有序性(這兩者的內(nèi)部實現(xiàn)都是紅黑樹),但其操作的時間復雜度都是O(log(n))的。如下是四者的對比:
操作unordered_map (C++)HashMap (Java)map (C++)TreeMap (Java)
是否可以保證數(shù)據(jù)的有序性?××
操作是否可以達到O(1)?××
操作時間復雜度O(1)O(1)O(log(n))O(log(n))

哈希表實現(xiàn)過程中一個很重要的問題是如何解決地址相同產(chǎn)生的沖突,一般來說,有下面四種方式:

(1)開放定址法:線性探測再散列、平方(二次)探測再散列、隨機探測再散列

(2)再哈希法

(3)鏈地址法

(4)建立一個公共溢出區(qū)

2 HashMap簡介

  • HashMap 是Java中對于哈希表的具體實現(xiàn),本文會比較詳細的介紹 Java 8 中對 HashMap 的主要函數(shù)的實現(xiàn)。
  • Java 8 中對 HashMap 做了全面的升級,其源碼也由原來的Java 7中的不足 1200行 變?yōu)榱爽F(xiàn)在的 2400 多行,可想而知, HashMap 從Java 7到Java 8結(jié)構(gòu)也發(fā)生了很大的變化。在Java 7中 HashMap 的具體實現(xiàn)是:數(shù)組+鏈表,Java 8中是:數(shù)組+鏈表+紅黑樹。如下是兩者的對比圖:

在這里插入圖片描述

Java 8中 HashMap 的定義如下(<K, V>代表使用到了泛型):

public class HashMap<K,V> extends AbstractMap<K,V>
    implements Map<K,V>, Cloneable, Serializable {
    // 具體實現(xiàn),省略......
}

繼承關(guān)系圖如下:

在這里插入圖片描述

HashMap 解決地址沖突采用的方式為:鏈地址法

本文著重分析 HashMap 中的兩個函數(shù):

// 添加鍵值對
public V put(K key, V value) { /* ... */ }
// 空間不足時,進行擴容的函數(shù)
final Node<K,V>[] resize() { /* ... */ }

另外,還需要提到的一點是: HashMap 不能保證多線程下的并發(fā)安全,如果想要在多線程下使用哈希表,請使用JUC(java.util.concurrent)包下的 ConcurrentdashMap ,這是一個并發(fā)安全的哈希表。Java 7中多線程下使用 HashMap 會導致的死循環(huán)

3 HashMap分析準備

3.1 HashMap中的常量與變量

在分析上面提到的那兩個函數(shù)之前,需要明確HashMap中的一些常量定義,方便之后的理解(注釋已由英語翻譯為漢語,如有錯誤請指正)

/**
 * 默認初始容量-必須是2的冪。
 */
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
/**
 * 最大容量,如果有參數(shù)的構(gòu)造函數(shù)隱式指定了更高的值,則使用。必須是2的冪且要小于等于1<<30。
 */
static final int MAXIMUM_CAPACITY = 1 << 30;
/**
 * 構(gòu)造函數(shù)中未指定時使用的負載因子。
 */
static final float DEFAULT_LOAD_FACTOR = 0.75f;
/**
 * bin(指table(HashMap中真正存儲數(shù)據(jù)的變量,下面有定義)中的某一項)中使用紅黑樹而不是鏈表的閾值。
 * 將元素添加到至少有這么多節(jié)點的bin時,容器將可能轉(zhuǎn)換為樹。
 * 該值必須大于2,并且至少應(yīng)為8,以滿足在紅黑樹中移除元素后轉(zhuǎn)換為鏈表的要求。
 */
static final int TREEIFY_THRESHOLD = 8;
/**
 * 在resize操作時樹退化成鏈表的閾值。應(yīng)小于TREEIFY_THRESHOLD,且最多6,以去匹配刪除元素時的收縮檢測。
 */
static final int UNTREEIFY_THRESHOLD = 6;
/**
 * 可將bin轉(zhuǎn)化為紅黑樹的最小表容量(否則(即table.length<該值),如果bin中的節(jié)點太多,則會調(diào)整table的大小)。
 * 應(yīng)至少為4 * TREEIFY_THRESHOLD,以避免調(diào)整大小和樹化閾值之間的沖突。
 */
static final int MIN_TREEIFY_CAPACITY = 64;
/* ---------------- Fields -------------- */
/**
 * table,在第一次使用時初始化(懶加載),并根據(jù)需要調(diào)整大小。
 * 分配時,長度總是2的冪(在某些操作中,我們也允許長度為零,以允許當前不需要的引導機制)
 */
transient Node<K,V>[] table;
/**
 * 保留緩存的entrySet()。請注意,AbstractMap字段用于keySet()和values()。
 */
transient Set<Map.Entry<K,V>> entrySet;
/**
 * 此映射中包含的鍵值映射數(shù)。
 */
transient int size;
/**
 * 此哈希映射在結(jié)構(gòu)上被修改的次數(shù)。
 * 結(jié)構(gòu)修改是指那些改變HashMap中映射的數(shù)量或以其他方式修改其內(nèi)部結(jié)構(gòu)的修改(例如,rehash)。
 * 此字段用于使HashMap的集合視圖上的迭代器快速失敗。(參見ConcurrentModificationException)。
 */
transient int modCount;
/**
 * 要調(diào)整大小的下一個大小值(capacity * load factor)。
 */
// (序列化后javadoc描述為true。此外,如果table數(shù)組尚未被分配,
//  則此字段保留初始數(shù)組容量,或零表示DEFAULT_INITIAL_CAPACITY)
int threshold;
/**
 * 哈希表的加載因子。
 */
final float loadFactor;

對常量的進一步理解

/**
 * 如果我們傳入的初始容量(記為a)不是2的指數(shù)次冪,會將變量threshold改成大于該數(shù)a且最接近這個數(shù)a的2的指數(shù)次冪。
 * 比如:Map<String, Double> t = new HashMap<>(13);
 *     執(zhí)行上面這句話后,threshold會被賦值為16(tableSizeFor()函數(shù)),因為懶加載,此時不會實例化table;
 *     之后put數(shù)據(jù)時會實例化table(長度為16),并將threshold賦值為12。
 *
 * 為什么數(shù)組table的長度一定要是2的冪次呢?
 * (1) 為了讓位運算 (n-1)&hash 達到取模(hash%n)的目的,加快運算速度;
 * (2) 更重要的一點是:要保證定位出來的值是在數(shù)組的長度之內(nèi)的,不能超出數(shù)組長度;
 * 	   并且減少哈希碰撞,讓每個位都可能被取到,例如:
 * 		正例:(16-1) & hash
 * 		二進制的15:  0000 0000 0000 1111
 * 		hash(隨機)   1101 0111 1011 0000
 * 		hash(隨機)   1101 0111 1011 1111
 * 		結(jié)果         0000 0000 0000 0001 ~ 0000 0000 0000 1111
 * 		即得出的索引下標只能在0~15之間,保證了所有索引都在數(shù)組長度的范圍內(nèi)而不會越界
 * 		并且由于2的指數(shù)次冪-1都是...1111的形式的,即最后一位是1
 * 		這樣,由于hash是隨機的,進行與運算后每一位都是能取到的
 * 		========================================================================
 * 		反例:(7-1) & hash
 * 		二進制6: 0000 0000 0000 0110
 * 		hash     1011 1001 0101 0000
 * 		hash     1001 0001 0000 1111
 * 		結(jié)果      0000 0000 0000 0000 ~ 0000 0000 0000 0110
 * 		即得出的索引范圍在0~6,雖然不會越界,但最后一位是0
 * 		即現(xiàn)在無論hash為何值,0001,0011,0101這幾個值是不可能取到的
 * 		這就加劇了hash碰撞,并且浪費了大量數(shù)組空間,顯然是我們不想看到的
 * (3) 讓resize()過程中不需要重新調(diào)用哈希函數(shù)計算哈希值,加快運行速度
 */
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
/**
 * 最大容量,如果有參數(shù)的構(gòu)造函數(shù)隱式指定了更高的值,則使用。必須是2的冪且要小于等于1<<30。
 */
static final int MAXIMUM_CAPACITY = 1 << 30;
/**
 * 默認的加載因子仍是0.75
 *
 * 為什么默認加載因子設(shè)為0.75?
 * 	  (1) 當加載因子比較大的時候:節(jié)省空間資源,耗費時間資源(鏈表查詢比較慢)
 *    (2) 當加載因子比較小的時候:節(jié)省時間資源,耗費空間資源
 *    綜合來看,0.75效果最好(可以認為這是JDK源碼工程師測試得到最好結(jié)果)
 */
static final float DEFAULT_LOAD_FACTOR = 0.75f;
/**
 * 樹閾值為8, 如果鏈表長度大于或等于8轉(zhuǎn)成紅黑樹
 *
 * 為什么樹閾值定為8?
 *    當put進來一個元素,通過hash算法,然后最后定位到同一個桶(鏈表)的概率p會隨著鏈表的長度k的增加而減少。
 * 	  根據(jù)jdk源碼注釋,這里p服從泊松分布:p = (exp(-0.5) * pow(0.5, k) / factorial(k))
 * 	  當這個鏈表長度為8的時候,這個概率幾乎接近于0(為0.00000006),所以我們才會將鏈表轉(zhuǎn)紅黑樹的臨界值定為8
 *    如下圖
 */
static final int TREEIFY_THRESHOLD = 8;
/**
 * 樹退化閾值為6,如果紅黑樹節(jié)點個數(shù)小于或等于6轉(zhuǎn)成鏈表
 *
 * 為什么樹退化閾值為6,而不是8?
 *    目的是為了防止復雜度震蕩。
 *    例如當前bin中有7個元素,此時不斷進行如下操作:添加一個元素后然后刪除(假設(shè)該元素也進入該bin中)
 *    則會出現(xiàn)不斷將鏈表變?yōu)榧t黑樹,然后紅黑樹變?yōu)殒湵淼牟僮?,復雜度飆升,產(chǎn)生震蕩。
 *    這是算法中常用的一種技巧,一種Lazy的技巧。同樣HashMap在創(chuàng)建的時候也采用了這種技巧,即懶加載。
 */
static final int UNTREEIFY_THRESHOLD = 6;
/**
 * 最小樹形化容量閾值64:即 當哈希表中的容量 >= 該值時,才允許樹形化鏈表 (即將鏈表轉(zhuǎn)換成紅黑樹)
 * 否則,若桶內(nèi)元素太多時,則直接擴容,而不是樹形化。
 * 目的是為了避免進行擴容、樹形化選擇的沖突,這個值不能小于 4 * TREEIFY_THRESHOLD
 */
static final int MIN_TREEIFY_CAPACITY = 64;

TREEIFY_tdRESHOLD=8對應(yīng)源碼中的解釋:

在這里插入圖片描述

3.2 HashMap中的節(jié)點定義

Node節(jié)點,用于存儲一個鍵值對的節(jié)點,也就是上面提到的 bin

/**
 * 基本哈希bin節(jié)點,用于大多數(shù)條目。(請參見下面的TreeNode子類,并在LinkedHashMap中查看其Entry子類。)
 */
static class Node<K,V> implements Map.Entry<K,V> {
    final int hash;
    final K key;
    V value;
    Node<K,V> next;
    Node(int hash, K key, V value, Node<K,V> next) {
        this.hash = hash;
        this.key = key;
        this.value = value;
        this.next = next;
    }
    public final K getKey()        { return key; }
    public final V getValue()      { return value; }
    public final String toString() { return key + "=" + value; }
    public final int hashCode() {
        return Objects.hashCode(key) ^ Objects.hashCode(value);
    }
    public final V setValue(V newValue) {
        V oldValue = value;
        value = newValue;
        return oldValue;
    }
    public final boolean equals(Object o) {
        if (o == this)
            return true;
        if (o instanceof Map.Entry) {
            Map.Entry<?,?> e = (Map.Entry<?,?>)o;
            if (Objects.equals(key, e.getKey()) &&
                Objects.equals(value, e.getValue()))
                return true;
        }
        return false;
    }
}

TreeNode節(jié)點,用于存儲紅黑樹中的節(jié)點,這個靜態(tài)內(nèi)部類在下面的分析中不重要,因此這里不給出定義,具體定義可以參照源碼。

4 put(K key, V value)函數(shù)分析

函數(shù)定義(注釋已由英語翻譯為漢語,之后的注釋都會被翻譯)

/**
 * 將指定值(value)與此映射中的指定鍵(key)相關(guān)聯(lián)。如果映射以前包含鍵(key)的映射,則舊的值(value)被替換
 *
 * @param key 與指定值關(guān)聯(lián)的鍵
 * @param value 要與指定鍵關(guān)聯(lián)的值
 * @return 與鍵關(guān)聯(lián)的上一個值,如果鍵沒有映射,則為null。(null返回也可以表示映射以前與null鍵關(guān)聯(lián)。)
 */
public V put(K key, V value) {
    return putVal(hash(key), key, value, false, true);
}

可以看到, put 函數(shù)調(diào)用了 hash 函數(shù)和 putVal 函數(shù),下面是這兩個函數(shù)的分析(分析在注釋中):

/**
 * 計算關(guān)鍵字.hashCode()并將散列的高位(異或)擴散到低位。
 * 由于table使用的掩碼是2的冪次,因此僅在當前掩碼上方的位上變化的哈希集將始終發(fā)生沖突。
 * (在已知的例子中有一組在小表格中保持連續(xù)整數(shù)的浮點鍵。)
 * 因此我們應(yīng)用了一種向下擴展高位影響的變換。在速度、實用性和位擴展質(zhì)量之間存在一種折衷。
 * 因為許多常見的散列集已經(jīng)被合理地分布(所以不能從傳播中受益),
 * 而且因為我們使用紅黑樹來處理容器中的大量沖突,所以我們只是以最便宜的方式對一些移位的位進行異或,
 * 以減少系統(tǒng)損失,以及合并最高位的影響,否則由于table邊界,這些最高位將永遠不會用于索引計算。
 */
static final int hash(Object key) {
    int h;
    // 為什么要這么做? 目的:降低hash沖突的幾率的同時保證計算哈希值的速度
    return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
/**
 * 實現(xiàn)Map.put和相關(guān)方法。
 *
 * @param hash 鍵的哈希值
 * @param key 鍵
 * @param value 要放置的值
 * @param onlyIfAbsent 如果為true,則不更改現(xiàn)有值(value)
 * @param evict 如果為false,則table處于創(chuàng)建模式。
 * @return 上一個值,如果沒有則為null
 */
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
               boolean evict) {
    Node<K,V>[] tab; Node<K,V> p; int n, i;
    // 1. 如果當前table為空,新建table
    //    如果是空參構(gòu)造器,默認table長度為16;
    //    如果指定大小,則table的長度為threshold(這個值已經(jīng)被tableSizeFor()函數(shù)變?yōu)?的冪)
    if ((tab = table) == null || (n = tab.length) == 0)
        n = (tab = resize()).length;
    // 2. 獲取當前key對應(yīng)的節(jié)點
    // 	  n-1 相當于掩碼,因為 n 是 2 的次冪,所以 n-1 二進制最低位有 k 個1(其中k=log2(n))
    //    (n-1)&hash 是待添加元素應(yīng)該存放的位置(有沖突的話使用鏈地址法解決)
    //    (n-1)&hash 相當于 hash%n(在n是2的冪次的情況下)
    //    例如 n=4, hash=10 : (n-1)&hash = 0011&1010 =2, 10%4=2
    if ((p = tab[i = (n - 1) & hash]) == null)
        // 3. 如果位置tab[(n-1)&hash]不存在節(jié)點,則新建節(jié)點
        tab[i] = newNode(hash, key, value, null);
    else {
        // 4. 位置tab[(n-1)&hash]存在節(jié)點,采用鏈地址法解決沖突
        Node<K,V> e; K k;
        // 5. key的hash相同 || key的引用相同或者key equals,則覆蓋原本的鍵值對中的值
        if (p.hash == hash && ((k = p.key) == key || (key != null && key.equals(k))))
            e = p;
        // 6. 如果當前節(jié)點(上面提到的bin)是一個紅黑樹節(jié)點(樹根),則將節(jié)點添加到紅黑樹中
        else if (p instanceof TreeNode)
            e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
        // 7. bin不是紅黑樹節(jié)點,也不是相同節(jié)點,則表示為bin為鏈表的頭結(jié)點
        else {
            // 8. 找到鏈表中最后的那個節(jié)點
            //    尾插法。不能采用頭插法,因為要比較插入的鍵(key)是否已經(jīng)存在,存在則替換,否則插入
            for (int binCount = 0; ; ++binCount) {
                if ((e = p.next) == null) {
                    p.next = newNode(hash, key, value, null);
                    // 9. 如果鏈表長度(包含當前還未插入的節(jié)點)大于或等于8轉(zhuǎn)成紅黑樹
                    if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
                        treeifyBin(tab, hash);
                    break;
                }
                // 10.如果鏈表中有相同的節(jié)點,則覆蓋
                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;
            // 是否替換掉value值
            if (!onlyIfAbsent || oldValue == null)
                e.value = value;
            afterNodeAccess(e);
            return oldValue;
        }
    }
    // 記錄修改次數(shù)
    ++modCount;
    // 是否超過容量,超過需要擴容
    if (++size > threshold)
        resize();
    afterNodeInsertion(evict);
    return null;
}

5 resize()函數(shù)分析

函數(shù)定義以及分析:

/**
 * 初始化 或 加倍table。
 * 如果為空,則根據(jù)字段threshold中的值初始化table大小。
 * 否則,因為我們使用的是倍增,所以每個bin中的元素要么留在同一索引中,要么在新表中以二次冪偏移量移動。
 *
 * @return the table
 */
final Node<K,V>[] resize() {
    Node<K,V>[] oldTab = table;
    int oldCap = (oldTab == null) ? 0 : oldTab.length;
    // 1.如果創(chuàng)建HashMap時調(diào)用的是有參函數(shù),第一次put時調(diào)用該函數(shù)時threshold不為0
    int oldThr = threshold;
    int newCap, newThr = 0;
    if (oldCap > 0) {  
        // 2.1 說明空間不足,需要擴增
        if (oldCap >= MAXIMUM_CAPACITY) {
            threshold = Integer.MAX_VALUE;
            return oldTab;
        } else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
                 oldCap >= DEFAULT_INITIAL_CAPACITY)
            newThr = oldThr << 1; // double threshold
    } else if (oldThr > 0) // initial capacity was placed in threshold
        // 2.2 有參函數(shù),第一次put進入這個分支
        newCap = oldThr;
    else {               // zero initial threshold signifies using defaults
        // 2.3 無參函數(shù),第一次put進入這個分支
        newCap = DEFAULT_INITIAL_CAPACITY;
        newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
    }
    // 3.有參函數(shù),第一次put這個判斷成立;否則不成立
    if (newThr == 0) {
        float ft = (float)newCap * loadFactor;
        newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ?
                  (int)ft : Integer.MAX_VALUE);
    }
    threshold = newThr;
    // 4.創(chuàng)建新的table用于存放數(shù)據(jù),有兩種情況
    //   (1) 懶加載,第一次put會初始化table (2) 倍增
    @SuppressWarnings({"rawtypes","unchecked"})
    Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];
    // 5.將新創(chuàng)建的數(shù)組newTab的引用賦值給字段table
    table = newTab;
    // 6.1 oldTab如果為null,說明是第一次put進入的該函數(shù),該函數(shù)作用是給table初始化
    if (oldTab != null) {  
        // 6.2 說明需要將oldTab中的數(shù)據(jù)遷移到newTab中
        for (int j = 0; j < oldCap; ++j) {
            Node<K,V> e;
            if ((e = oldTab[j]) != null) {
                oldTab[j] = null;
                if (e.next == null)
                    // 7.1 說明這個bin中只有一個數(shù)據(jù),直接移入新數(shù)組中即可
                    newTab[e.hash & (newCap - 1)] = e;
                else if (e instanceof TreeNode)
                    // 7.2 說明這個bin是紅黑樹的根節(jié)點,......(不是重點,略過)
                    ((TreeNode<K,V>)e).split(this, newTab, j, oldCap);
                else { // preserve order
                    // 7.3 說明這個bin是鏈表的頭結(jié)點,需要將高位和低位斷開
                    //     將一個鏈表分為兩組,縮短了鏈表,提高了性能
                    /*
                     * 假設(shè) oldCap=16,鏈表中存在兩個哈希值,hash1=0b0 1010,hash2=0b1 1010
                     * 此時的j=0b1010,這兩個值最關(guān)鍵之處在于最高位值不同,將會被分到低位和高位兩個索引處
                     * 				  hash1			hash2
                     * hash值:       0 1010	   1 1010
                     * oldCap(16)    1 0000		   1 0000
                     * 				 0 0000		   1 0000
                     * hash1對應(yīng)的節(jié)點將會被放入newTab[j]中,hash2對應(yīng)的節(jié)點將會被放入newTab[j+oldCap]中
                     * ‘與'運算的結(jié)果,只可能有兩種值:0 或者 16
                     * 也就是說當前節(jié)點用當前節(jié)點的hash值和舊數(shù)組的長度(16)做'與'運算的結(jié)果只可能是0或16
                     */
                    Node<K,V> loHead = null, loTail = null;
                    Node<K,V> hiHead = null, hiTail = null;
                    Node<K,V> next;
                    do {
                        next = e.next;
                        if ((e.hash & oldCap) == 0) {  // 如果是0,則使用低位的指針
                            if (loTail == null) loHead = e;
                            else loTail.next = e;  // 尾插法
                            loTail = e;
                        }
                        else {  // 如果是16,則使用高位的指針
                            if (hiTail == null) hiHead = e;
                            else hiTail.next = e;  // 尾插法
                            hiTail = e;
                        }
                    } while ((e = next) != null);
                    if (loTail != null) {
                        loTail.next = null;  // 把低位的尾部節(jié)點的next值為空(先將高位和低位斷開)
                        newTab[j] = loHead;  // 將低位的頭部賦給新數(shù)組的某個值,也就是將高位的所有節(jié)點移動過去
                    }
                    if (hiTail != null) {
                        hiTail.next = null;  // 把高位的尾部節(jié)點的next值為空
                        // 再將高位的頭部放到新數(shù)組的j + oldCap索引處(當前索引+舊數(shù)組的長度)
                        // 比如說現(xiàn)在的索引是3,再加上數(shù)組長度16,最后就是將高位放到新數(shù)組的索引為19的地方去
                        newTab[j + oldCap] = hiHead;
                    }
                }
            }
        }
    }
    return newTab;
}

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

相關(guān)文章

  • java實現(xiàn)小球碰撞功能

    java實現(xiàn)小球碰撞功能

    這篇文章主要為大家詳細介紹了java實現(xiàn)小球碰撞功能,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-01-01
  • IntelliJ IDEA全局內(nèi)容搜索和替換教程圖解

    IntelliJ IDEA全局內(nèi)容搜索和替換教程圖解

    很多朋友在做項目時,會在整個項目里活指定文件夾下進行全局搜索和替換,下面小編給大家?guī)砹薎ntelliJ IDEA全局內(nèi)容搜索和替換教程圖解,需要的朋友參考下吧
    2018-04-04
  • 部署springboot項目到云服務(wù)器的兩種方式(jar+war)

    部署springboot項目到云服務(wù)器的兩種方式(jar+war)

    本文主要介紹了部署springboot項目到云服務(wù)器的兩種方式,主要介紹了jar和war兩種方式,文中通過示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-12-12
  • java實現(xiàn)的冒泡排序算法示例

    java實現(xiàn)的冒泡排序算法示例

    這篇文章主要介紹了java實現(xiàn)的冒泡排序算法,結(jié)合實例形式分析了冒泡排序算法的具體操作步驟與實現(xiàn)技巧,需要的朋友可以參考下
    2017-01-01
  • 通過實例學習Either 樹和模式匹配

    通過實例學習Either 樹和模式匹配

    這篇文章主要介紹了通過實例學習Either 樹和模式匹配,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,,需要的朋友可以參考下
    2019-06-06
  • 關(guān)于List、Map、Stream初始化方式

    關(guān)于List、Map、Stream初始化方式

    這篇文章主要介紹了關(guān)于List、Map、Stream初始化方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-08-08
  • Java GZIPOutputStream流壓縮文件的操作

    Java GZIPOutputStream流壓縮文件的操作

    這篇文章主要介紹了Java GZIPOutputStream流壓縮文件的操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2021-02-02
  • Spring Cloud 動態(tài)刷新配置信息教程詳解

    Spring Cloud 動態(tài)刷新配置信息教程詳解

    這篇文章主要介紹了Spring Cloud 動態(tài)刷新配置信息的教程,非常不錯,具有一定的參考借鑒價值,需要的朋友可以參考下
    2018-06-06
  • Java Selenium實現(xiàn)多窗口切換的示例代碼

    Java Selenium實現(xiàn)多窗口切換的示例代碼

    這篇文章主要介紹了Java Selenium實現(xiàn)多窗口切換的示例代碼,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2020-09-09
  • SpringCloud超詳細講解Feign聲明式服務(wù)調(diào)用

    SpringCloud超詳細講解Feign聲明式服務(wù)調(diào)用

    Feign可以把Rest的請求進行隱藏,偽裝成類似Spring?MVC的Controller一樣。不用再自己拼接url,拼接參數(shù)等等操作,一切都交給Feign去做
    2022-06-06

最新評論