HashMap原理

1 Hash算法

哈希函數(shù)的目標(biāo)是計算key在數(shù)組中的下標(biāo)聋袋。判斷一個哈希函數(shù)的標(biāo)準(zhǔn)是:散列是否均勻、計算是否簡單贷币。

HashMap哈希函數(shù)的步驟:

  1. 對key對象的hashcode進行擾動

  2. 通過取模求得數(shù)組下標(biāo)

擾動是為了讓hashcode的隨機性更高掠廓,第二步取模就不會讓所以的key都聚集在一起,提高散列均勻度苔严。

static final int hash(Object key) {
    int h;
    // 獲取到key的hashcode定枷,在高低位異或運算
    return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}

也就是低16位是和高16位進行異或,高16位保持不變届氢。一般的數(shù)組長度都會比較短欠窒,取模運算中只有低位參與散列;高位與低位進行異或退子,讓高位也得以參與散列運算岖妄,使得散列更加均勻。

2 HashMap的數(shù)組長度

HashMap的默認(rèn)數(shù)組長度的16寂祥,如果給于了初始長度荐虐,那么將計算出大于等于當(dāng)前給定數(shù)組長度的最小2的冪次方的數(shù),如果給定的數(shù)組長度超過MAXIMUM_CAPAC丸凭,則用MAXIMUM_CAPAC作為數(shù)組長度福扬。

//創(chuàng)建 HashMap 時未指定初始容量情況下的默認(rèn)容量   
   static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; 

//HashMap 的最大容量
   static final int MAXIMUM_CAPACITY = 1 << 30;

規(guī)格化數(shù)組長度

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;
}

當(dāng)給定初始長度時,會調(diào)用如上方法將HashMap的數(shù)組長度設(shè)置為2的冪次方惜犀。上面一連串的位移操作是為了把n的最高位1后面的位都變成1铛碑。這樣就是得到一尾數(shù)連續(xù)為1的數(shù),通過n+1操作就得到一個2的冪次方的數(shù)虽界。

注意:

n=cap-1是為了當(dāng)給的初始長度是2的冪次方時汽烦,讓結(jié)果等于當(dāng)前給定的初始長度。

3 HashMap定位

HashMap控制數(shù)組長度為2的整數(shù)次冪莉御,好處是對hashcode進行求余運算和讓hashcode與數(shù)組長度-1進行位與運算是相同的效果撇吞。能夠快速定位Key所在的數(shù)組的位置俗冻,HashMap通過如下方法快速定位。

4 Hash擴容

HashMap每次擴容都使當(dāng)前數(shù)組長度變成原數(shù)組長度的2倍(除最大容量外)梢夯。

如上(a)為原數(shù)組長度言疗,(b)為擴容后的數(shù)組長度,可以看出颂砸,當(dāng)擴容后數(shù)組原位置上的Key將落在新數(shù)組的原位置或原位置+原數(shù)組長度噪奄。所有擴容后數(shù)據(jù)的遷移就變得很高效了,只需要判斷Key的Hash&原數(shù)組長度n就知道Key在擴容后的位置人乓。

5 HashMap數(shù)據(jù)結(jié)構(gòu)

   //HashMap 默認(rèn)的裝載因子,當(dāng) HashMap 中元素數(shù)量超過 容量*裝載因子 時勤篮,進行 resize() 操作
   static final float DEFAULT_LOAD_FACTOR = 0.75f;

   //用來確定何時將解決 hash 沖突的鏈表轉(zhuǎn)變?yōu)榧t黑樹
   static final int TREEIFY_THRESHOLD = 8;

   // 用來確定何時將解決 hash 沖突的紅黑樹轉(zhuǎn)變?yōu)殒湵?   static final int UNTREEIFY_THRESHOLD = 6;

   /* 當(dāng)需要將解決 hash 沖突的鏈表轉(zhuǎn)變?yōu)榧t黑樹時,需要判斷下此時數(shù)組容量色罚,若是由于數(shù)組容量太信龅蕖(小于 MIN_TREEIFY_CAPACITY )導(dǎo)致的 hash沖突太多戳护,則不進行鏈表轉(zhuǎn)變?yōu)榧t黑樹操作金抡,轉(zhuǎn)為利用 resize() 函數(shù)對 hashMap 擴容 */
   static final int MIN_TREEIFY_CAPACITY = 64;

HashMap的裝載因子默認(rèn)為0.75,是為了權(quán)衡效率和空間腌且。當(dāng)裝載因子越小時梗肝,Hash沖突就越小,但占用空間就越大铺董。

JDK1.8HashMap引入了紅黑樹結(jié)構(gòu)巫击,當(dāng)鏈表長度過長時,將鏈表轉(zhuǎn)換為紅黑樹精续,提高鏈表查詢效率坝锰。當(dāng)鏈表長度大于等于8時,會把鏈表轉(zhuǎn)換為紅黑樹重付。當(dāng)鏈表長度等于小于等于6時,會把紅黑樹轉(zhuǎn)換成鏈表确垫。

為什么在8和6的時候轉(zhuǎn)換?

因為當(dāng)負(fù)載因子是0.75時森爽,泊松分布中鏈表長度為8的概率已經(jīng)很小了嚣镜,為什么在6的時候轉(zhuǎn)換為鏈表,一是因為6和8的概率相差較大菊匿,二是防止在8的時候?qū)ν粋€元素平凡的添加和刪除计福,導(dǎo)致鏈表在長度在8與7之間平凡轉(zhuǎn)換導(dǎo)致效率低下。

     * 0:    0.60653066
     * 1:    0.30326533
     * 2:    0.07581633
     * 3:    0.01263606
     * 4:    0.00157952
     * 5:    0.00015795
     * 6:    0.00001316
     * 7:    0.00000094
     * 8:    0.00000006

鏈表轉(zhuǎn)換成紅黑樹還有一個限制條件:

當(dāng)鏈表要轉(zhuǎn)換為紅黑樹時徽职,首先要判斷當(dāng)前數(shù)組長度是否大于64象颖,如果小于,就進行擴容姆钉。

為什么做這一個判讀说订,是因為,數(shù)組長度較小時潮瓶,如果鏈表長度能達(dá)到8陶冷,那么離數(shù)組擴容也不遠(yuǎn)了,所有先擴容毯辅,防止轉(zhuǎn)換后在擴容埂伦,導(dǎo)致效率低下。

5.1Node節(jié)點
// Node<K,V> 類用來實現(xiàn)數(shù)組及鏈表的數(shù)據(jù)結(jié)構(gòu)
  static class Node<K,V> implements Map.Entry<K,V> {
        final int hash; //保存節(jié)點的 hash 值
        final K key; //保存節(jié)點的 key 值
        V value; //保存節(jié)點的 value 值
        Node<K,V> next; //指向鏈表結(jié)構(gòu)下的當(dāng)前節(jié)點的 next 節(jié)點思恐,紅黑樹 TreeNode 節(jié)點中也有用到
 }
// TreeNode<K,V> 繼承 LinkedHashMap.Entry<K,V>沾谜,用來實現(xiàn)紅黑樹相關(guān)的存儲結(jié)構(gòu)
    static final class TreeNode<K,V> extends LinkedHashMap.Entry<K,V> {
        TreeNode<K,V> parent;  // 存儲當(dāng)前節(jié)點的父節(jié)點
        TreeNode<K,V> left; //存儲當(dāng)前節(jié)點的左孩子
        TreeNode<K,V> right; //存儲當(dāng)前節(jié)點的右孩子
        TreeNode<K,V> prev;    // 存儲當(dāng)前節(jié)點的前一個節(jié)點
        boolean red; // 存儲當(dāng)前節(jié)點的顏色(紅、黑)
        TreeNode(int hash, K key, V val, Node<K,V> next) {
            super(hash, key, val, next);
   }

注意:

TreeNode還有prev胀莹,next兩個引用基跑,保留了雙向鏈表的特性,在數(shù)組擴容時嗜逻,方便紅黑樹的遍歷涩僻。

5.2 put方法
//指定節(jié)點 key,value,向 hashMap 中插入節(jié)點
public V put(K key, V value) {
   //注意待插入節(jié)點 hash 值的計算栈顷,調(diào)用了 hash(key) 函數(shù)
 //實際調(diào)用 putVal()進行節(jié)點的插入
      return putVal(hash(key), key, value, false, true);
  }

  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;
  /*根據(jù) hash 值確定節(jié)點在數(shù)組中的插入位置逆日,若此位置沒有元素則進行插入,注意確定插入位置所用的計算方法為 (n - 1) & hash,由于 n 一定是2的冪次萄凤,這個操作相當(dāng)于
hash % n */
      if ((p = tab[i = (n - 1) & hash]) == null)
          tab[i] = newNode(hash, key, value, null);
      else {//說明待插入位置存在元素
          Node<K,V> e; K k;
       //比較原來元素與待插入元素的 hash 值和 key 值
          if (p.hash == hash &&
              ((k = p.key) == key || (key != null && key.equals(k))))
              e = p;
       //若原來元素是紅黑樹節(jié)點室抽,調(diào)用紅黑樹的插入方法:putTreeVal
          else if (p instanceof TreeNode)
              e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
          else {//證明原來的元素是鏈表的頭結(jié)點,從此節(jié)點開始向后尋找合適插入位置
              for (int binCount = 0; ; ++binCount) {
                  if ((e = p.next) == null) {
      //找到插入位置后靡努,新建節(jié)點插入
                      p.next = newNode(hash, key, value, null);
      //若鏈表上節(jié)點超過TREEIFY_THRESHOLD - 1坪圾,將鏈表變?yōu)榧t黑樹
                      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;
              }
          }//end else
          if (e != null) { // 待插入元素在 hashMap 中已存在
              V oldValue = e.value;
              if (!onlyIfAbsent || oldValue == null)
                  e.value = value;
              afterNodeAccess(e);
              return oldValue;
          }
      }//end else
      ++modCount;
      if (++size > threshold)
          resize();
      afterNodeInsertion(evict);
      return null;
  }//end putval

插入流程:

  1. 如果數(shù)組沒有初始化就是初始化數(shù)組

  2. 定位數(shù)組中的位置,如果當(dāng)前位置沒有數(shù)據(jù)惑朦,就直接插入兽泄。

  3. 當(dāng)前位置存在數(shù)據(jù)

    • 如果是TreeNode直接插入

    • 如果是Node,判斷是否存在當(dāng)前Key漾月,如果存在則替換值病梢,否則插入鏈表的尾部,然后判斷是否需要轉(zhuǎn)換成紅黑樹。

  4. 判斷數(shù)組是否需要擴容蜓陌。

注意:

JDK1.7使用頭插觅彰,雖然效率高,但是在多線程下擴容會形成環(huán)钮热。JDK1.8使用尾插填抬,然后可以避免多線程形成環(huán),但是數(shù)據(jù)會丟失隧期,使用尾插是為了統(tǒng)計鏈表長度,判斷是否需要轉(zhuǎn)換為紅黑樹读拆。

5.3 resize方法
final Node<K,V>[] resize() {        
    Node<K,V>[] oldTab = table;
        int oldCap = (oldTab == null) ? 0 : oldTab.length;
        int oldThr = threshold;
        int newCap, newThr = 0;
  /*
        1鸵闪、resize()函數(shù)在size > threshold時被調(diào)用蚌讼。
            oldCap大于 0 代表原來的 table 表非空, oldCap 為原表的大小篡石,
            oldThr(threshold) 為 oldCap × load_factor
     */
        if (oldCap > 0) {
            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
        }
    /*
        2凰萨、resize()函數(shù)在table為空被調(diào)用胖眷。
        oldCap 小于等于 0 且 oldThr 大于0,代表用戶創(chuàng)建了一個 HashMap珊搀,但是使用的構(gòu)造函數(shù)為
        HashMap(int initialCapacity, float loadFactor) 或 HashMap(int initialCapacity)
        或 HashMap(Map<? extends K, ? extends V> m)境析,導(dǎo)致 oldTab 為 null,oldCap 為0劳淆,
        oldThr 為用戶指定的 HashMap的初始容量沛鸵。
      */
        else if (oldThr > 0) // initial capacity was placed in threshold
            newCap = oldThr;
     /*
            3、resize()函數(shù)在table為空被調(diào)用朝刊。
            oldCap 小于等于 0 且 oldThr 等于0拾氓,用戶調(diào)用 HashMap()構(gòu)造函數(shù)創(chuàng)建的 HashMap底哥,所有值均采用默認(rèn)值趾徽,
          oldTab(Table)表為空,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;        
        Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];
        table = newTab;
        if (oldTab != null) {
       //把 oldTab 中的節(jié)點 reHash 到 newTab 中去
            for (int j = 0; j < oldCap; ++j) {
                Node<K,V> e;
                if ((e = oldTab[j]) != null) {
                    oldTab[j] = null;
            //若節(jié)點是單個節(jié)點载绿,直接在 newTab 中進行重定位
                    if (e.next == null)
                        newTab[e.hash & (newCap - 1)] = e;
            //若節(jié)點是 TreeNode 節(jié)點崭庸,要進行 紅黑樹的 rehash 操作
                    else if (e instanceof TreeNode)
                        ((TreeNode<K,V>)e).split(this, newTab, j, oldCap);
            //若是鏈表,進行鏈表的 rehash 操作
                    else { // preserve order
                        Node<K,V> loHead = null, loTail = null;
                        Node<K,V> hiHead = null, hiTail = null;
                        Node<K,V> next;
                        do {
                              next = e.next;
                  //根據(jù)算法 e.hash & oldCap 判斷節(jié)點位置 rehash 后是否發(fā)生改變
                            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;
                // rehash 后節(jié)點新的位置一定為原來基礎(chǔ)上加上 oldCap
                            newTab[j + oldCap] = hiHead;
                        }
                    }
                }
            }
        }
        return newTab;
    }

擴容流程:

  1. 首先確定新數(shù)組的大小

    • 如果數(shù)組長度大于等于最大容量就不擴容

    • 如果擴容后的數(shù)組大于等于最大容量,就讓擴容后的數(shù)組長度設(shè)置為最大容量熬粗。

    • 否則就是原來容量的2倍(這里沒有解析初始化數(shù)組驻呐,數(shù)組初始化也會擴容)

  2. 創(chuàng)建新的數(shù)組,遍歷的就的數(shù)據(jù)進行數(shù)組遷移

    • 當(dāng)前位置是TreeNode含末,則調(diào)用TreeNode的split方法

    • 當(dāng)前位置是Node佣盒,遍歷鏈表,將鏈表放置在新數(shù)組的當(dāng)前位置或者紊搪,當(dāng)前位置+原數(shù)組長度的位置全景。

5.4 split方法
//這個函數(shù)的功能是對紅黑樹進行 rehash 操作
final void split(HashMap<K,V> map, Node<K,V>[] tab, int index, int bit) {
        TreeNode<K,V> b = this;
        // Relink into lo and hi lists, preserving order
        TreeNode<K,V> loHead = null, loTail = null;
        TreeNode<K,V> hiHead = null, hiTail = null;
        int lc = 0, hc = 0;
     //由于 TreeNode 節(jié)點之間存在雙端鏈表的關(guān)系,可以利用鏈表關(guān)系進行 rehash
        for (TreeNode<K,V> e = b, next; e != null; e = next) {
            next = (TreeNode<K,V>)e.next;
            e.next = null;
            if ((e.hash & bit) == 0) {
                if ((e.prev = loTail) == null)
                    loHead = e;
                else
                    loTail.next = e;
                loTail = e;
                ++lc;
            }
            else {
                if ((e.prev = hiTail) == null)
                    hiHead = e;
                else
                    hiTail.next = e;
                hiTail = e;
                ++hc;
            }
        }

        //rehash 操作之后注意對根據(jù)鏈表長度進行 untreeify 或 treeify 操作
        if (loHead != null) {
            if (lc <= UNTREEIFY_THRESHOLD)
                tab[index] = loHead.untreeify(map);
            else {
                tab[index] = loHead;
                if (hiHead != null) // (else is already treeified)
                    loHead.treeify(tab);
            }
        }
        if (hiHead != null) {
            if (hc <= UNTREEIFY_THRESHOLD)
                tab[index + bit] = hiHead.untreeify(map);
            else {
                tab[index + bit] = hiHead;
                if (loHead != null)
                    hiHead.treeify(tab);
            }
        }//end if
    }//end split

注意:

由于TreeNode維持了雙向鏈表的特征,所以數(shù)據(jù)遷移和單鏈表一樣炕贵,將數(shù)據(jù)遷移到新數(shù)組的當(dāng)前位置或這當(dāng)前位置+原數(shù)組長度的位置称开。最后判斷新鏈表長度,如果大于等于8則轉(zhuǎn)換成紅黑樹钥弯,如果小于等于6轉(zhuǎn)換成鏈表脆霎。當(dāng)HashMap在刪除節(jié)點時总处,不會判斷長度睛蛛,也不會將紅黑樹轉(zhuǎn)換為鏈表

厲害了忆肾!把 HashMap 剖析的只剩渣了!_良月柒-CSDN博客

深入理解HashMap底層原理剖析(JDK1.8)-黑馬程序員技術(shù)交流社區(qū)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末旭从,一起剝皮案震驚了整個濱河市场仲,隨后出現(xiàn)的幾起案子渠缕,更是在濱河造成了極大的恐慌,老刑警劉巖馍忽,帶你破解...
    沈念sama閱讀 211,123評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異坝冕,居然都是意外死亡徽诲,警方通過查閱死者的電腦和手機吵血,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,031評論 2 384
  • 文/潘曉璐 我一進店門蹋辅,熙熙樓的掌柜王于貴愁眉苦臉地迎上來侦另,“玉大人褒傅,你說我怎么就攤上這事袄友。” “怎么了支竹?”我有些...
    開封第一講書人閱讀 156,723評論 0 345
  • 文/不壞的土叔 我叫張陵礼搁,是天一觀的道長馒吴。 經(jīng)常有香客問我瑟曲,道長,這世上最難降的妖魔是什么莹捡? 我笑而不...
    開封第一講書人閱讀 56,357評論 1 283
  • 正文 為了忘掉前任篮赢,我火速辦了婚禮,結(jié)果婚禮上涣脚,老公的妹妹穿的比我還像新娘。我一直安慰自己遣蚀,他們只是感情好芭梯,可當(dāng)我...
    茶點故事閱讀 65,412評論 5 384
  • 文/花漫 我一把揭開白布弄喘。 她就那樣靜靜地躺著蘑志,像睡著了一般。 火紅的嫁衣襯著肌膚如雪澎媒。 梳的紋絲不亂的頭發(fā)上波桩,一...
    開封第一講書人閱讀 49,760評論 1 289
  • 那天柏卤,我揣著相機與錄音匀油,去河邊找鬼。 笑死桥滨,一個胖子當(dāng)著我的面吹牛齐媒,可吹牛的內(nèi)容都是我干的纷跛。 我是一名探鬼主播贫奠,決...
    沈念sama閱讀 38,904評論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼脖律!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起腕侄,我...
    開封第一講書人閱讀 37,672評論 0 266
  • 序言:老撾萬榮一對情侶失蹤小泉,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后冕杠,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體微姊,經(jīng)...
    沈念sama閱讀 44,118評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,456評論 2 325
  • 正文 我和宋清朗相戀三年分预,在試婚紗的時候發(fā)現(xiàn)自己被綠了柒桑。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,599評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡噪舀,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出飘诗,到底是詐尸還是另有隱情与倡,我是刑警寧澤莉给,帶...
    沈念sama閱讀 34,264評論 4 328
  • 正文 年R本政府宣布交煞,位于F島的核電站溉潭,受9級特大地震影響馋贤,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜犹芹,卻給世界環(huán)境...
    茶點故事閱讀 39,857評論 3 312
  • 文/蒙蒙 一屿笼、第九天 我趴在偏房一處隱蔽的房頂上張望志电。 院中可真熱鬧,春花似錦鱼蝉、人聲如沸羔挡。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,731評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽轮蜕。三九已至税课,卻和暖如春韩玩,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背佛析。 一陣腳步聲響...
    開封第一講書人閱讀 31,956評論 1 264
  • 我被黑心中介騙來泰國打工档冬, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留披坏,地道東北人玫氢。 一個月前我還...
    沈念sama閱讀 46,286評論 2 360
  • 正文 我出身青樓灰殴,卻偏偏與公主長得像掰伸,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,465評論 2 348

推薦閱讀更多精彩內(nèi)容

  • 散列表 定義:通過散列函數(shù)把元素的鍵值映射為下標(biāo)催跪,然后將數(shù)據(jù)存儲在數(shù)組中對應(yīng)下標(biāo)的位置。當(dāng)我們按照鍵值查詢元素時,...
    回憶只能等候閱讀 128評論 0 0
  • HashMap與Hashtable兩個類都是通過Key-Value對存儲的數(shù)據(jù)結(jié)構(gòu)推姻。根據(jù)官方的說法,二者唯二的區(qū)別...
    littlefogcat閱讀 323評論 0 1
  • 轉(zhuǎn)載于:https://segmentfault.com/a/1190000012926722 1.概述 本篇文章...
    無色的葉閱讀 732評論 2 7
  • 前言: 本文基于 JDK1.8,不會過多的擴展其它知識机久,重點關(guān)注 HashMap 的實現(xiàn)。 首先簡單介紹一下和 H...
    M_lear閱讀 2,236評論 0 3
  • 今天感恩節(jié)哎,感謝一直在我身邊的親朋好友。感恩相遇!感恩不離不棄。 中午開了第一次的黨會服球,身份的轉(zhuǎn)變要...
    迷月閃星情閱讀 10,556評論 0 11