HashMap源碼解析

1.獲取 index雏门,有關(guān)鍵的以下兩步

參考文檔:https://blog.csdn.net/supercmd/article/details/100042302

1.1 擾動(dòng)函數(shù)

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

解讀:

  • 為什么要用擾動(dòng)函數(shù)茁影?

答:擾動(dòng)函數(shù)就是解決碰撞問題呼胚。若不使用擾動(dòng)函數(shù)蝇更,則直接將key.hashCode()和下面的步驟2做與運(yùn)算,則會(huì)有以下情景访圃。

以初始長(zhǎng)度16為例况脆,16-1=15。2進(jìn)制表示是00000000 00000000 00001111看铆。和某散列值做“與”操作如下,結(jié)果就是截取了最低的四位值棠隐。

image
      10100101 11000100 00100101
   &  00000000 00000000 00001111
   ------------------------------
      00000000 00000000 00000101 

這樣就算散列值分布再松散助泽,要是只取最后幾位的話,碰撞也會(huì)很嚴(yán)重。如果散列本身做得不好厢漩,分布上成等差數(shù)列的漏洞溜嗜,恰好使最后幾個(gè)低位呈現(xiàn)規(guī)律性重復(fù)辟躏,則碰撞會(huì)更嚴(yán)重捎琐。

  • 擾動(dòng)函數(shù)是怎么實(shí)現(xiàn)的瑞凑?

由擾動(dòng)函數(shù)源碼可知籽御,會(huì)有以下步驟:

①使用key.hashCode()計(jì)算hash值并賦值給變量h铃将;

②將h向右移動(dòng)16位;

③將變量h和向右移16位的h做異或運(yùn)算(二進(jìn)制位相同為0哪工,不同為1)雁比。此時(shí)得到經(jīng)過擾動(dòng)函數(shù)后的hash值偎捎。圖解如下:

image
  h = hashCode():    1111 1111 1111 1111 1111 0000 1110 1010 
-----------------------------------------------------------
                h:   1111 1111 1111 1111 1111 0000 1110 1010 
           h>>>16:   0000 0000 0000 0000 1111 1111 1111 1111
hash = h^(h>>>16):   1111 1111 1111 1111 0000 1111 0001 0101  高16位和低16位 異或運(yùn)算
-----------------------------------------------------------
       (n-1)&hash:   0000 0000 0000 0000 0000 0000 0000 1111  與運(yùn)算
                     1111 1111 1111 1111 0000 1111 0001 0101
-------------------------------------------------------------
                     0000 0000 0000 0000 0000 0000 0000 0101 = 5
                         
  • 為什么要將key.hashCode()右移16位?

右移16位正好為32bit的一半丈牢,自己的高半?yún)^(qū)和低半?yún)^(qū)做異或己沛,是為了混合原始哈希嗎的高位和低位申尼,來加大低位的隨機(jī)性师幕。而且混合后的低位摻雜了高位的部分特征,使高位的信息也被保留下來

1.2 與運(yùn)算


int index = h & (length - 1)蒙挑;  //確定index數(shù)組下標(biāo)

  • 為什么要使用與運(yùn)算忆蚀?

若直接使用key.hashCode()計(jì)算出hash值馋袜,則范圍為:-2147483648到2147483648察皇,大約40億的映射空間什荣。若映射得比較均勻,是很難出現(xiàn)碰撞的桅锄。但是這么大范圍無法放入內(nèi)存中,況且HashMap的 初始容量為16檐束。所以必須要進(jìn)行與運(yùn)算取模茶没。

1.3 如何保證HashMap的容量是2的倍數(shù)

   /**
     * Returns a power of two size for the given target capacity.
     * 返回給定目標(biāo)容量大小的2次方。
     */
    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;
    }

之所以在開始移位前先將容量-1,是為了避免給定容量已經(jīng)是8,16這樣2的冪時(shí)廊移,不減一直接移位會(huì)導(dǎo)致得到的結(jié)果比預(yù)期大狡孔。比如預(yù)期16得到應(yīng)該是16殃恒,直接移位的話會(huì)得到32离唐。

參考文檔:https://www.cnblogs.com/xiyixiaodao/p/14483876.html

2.HashMap底層數(shù)據(jù)結(jié)構(gòu)

  • JDK1.8之前的HashMap底層數(shù)據(jù)結(jié)構(gòu)
  • 數(shù)組+鏈表
jdk1.8之前的內(nèi)部結(jié)構(gòu)
  • JDK1.8之后的HashMap底層數(shù)據(jù)結(jié)構(gòu)
  • 數(shù)組+鏈表+紅黑樹
  • 鏈表長(zhǎng)度>=8 && 數(shù)組長(zhǎng)度>=64 鏈表轉(zhuǎn)化為紅黑樹
  • 鏈表長(zhǎng)度>=8 && 數(shù)組長(zhǎng)度< 64 擴(kuò)容完沪,原來數(shù)組長(zhǎng)度*2
  • 紅黑樹節(jié)點(diǎn)<=6,自動(dòng)轉(zhuǎn)化為鏈表
JDK1.8之后的HashMap底層數(shù)據(jù)結(jié)構(gòu)

HashMap 的長(zhǎng)度為什么是2的冪次方

為了加快哈希計(jì)算以及減少哈希沖突

  • 為什么可以加快計(jì)算嵌戈?

我們都知道為了找到 KEY 的位置在哈希表的哪個(gè)槽里面覆积,需要計(jì)算 hash(KEY) % 數(shù)組長(zhǎng)度

但是!% 計(jì)算比 & 慢很多

所以用 & 代替 %熟呛,為了保證 & 的計(jì)算結(jié)果等于 % 的結(jié)果需要把 length 減 1

hash%length==hash&(length-1)的前提是 length 是2的 n 次方

  • 為什么可以減少?zèng)_突宽档?

假設(shè)現(xiàn)在數(shù)組的長(zhǎng)度 length 可能是偶數(shù)也可能是奇數(shù)

length 為偶數(shù)時(shí),length-1 為奇數(shù)惰拱,奇數(shù)的二進(jìn)制最后一位是 1雌贱,這樣便保證了 hash &(length-1) 的最后一位可能為 0欣孤,也可能為 1(這取決于 h 的值),即 & 運(yùn)算后的結(jié)果可能為偶數(shù)段只,也可能為奇數(shù),這樣便可以保證散列的均勻性。

而如果 length 為奇數(shù)的話,很明顯 length-1 為偶數(shù),它的最后一位是 0,這樣 hash & (length-1) 的最后一位肯定為 0,即只能為偶數(shù)钾恢,這樣任何 hash 值都只會(huì)被散列到數(shù)組的偶數(shù)下標(biāo)位置上崩哩,這便浪費(fèi)了近一半的空間

因此,length 取 2 的整數(shù)次冪,是為了使不同 hash 值發(fā)生碰撞的概率較小奖年,這樣就能使元素在哈希表中均勻地散列水评。

解釋:

為了能讓 HashMap 存取高效拿霉,盡量較少碰撞偏瓤,也就是要盡量把數(shù)據(jù)分配均勻。我們上面也講到了過了,Hash 值的范圍值-2147483648到2147483647辛藻,前后加起來大概40億的映射空間纺蛆,只要哈希函數(shù)映射得比較均勻松散凤藏,一般應(yīng)用是很難出現(xiàn)碰撞的桨昙。但問題是一個(gè)40億長(zhǎng)度的數(shù)組,內(nèi)存是放不下的。所以這個(gè)散列值是不能直接拿來用的。用之前還要先做對(duì)數(shù)組的長(zhǎng)度取模運(yùn)算,得到的余數(shù)才能用來要存放的位置也就是對(duì)應(yīng)的數(shù)組下標(biāo)市怎。這個(gè)數(shù)組下標(biāo)的計(jì)算方法是“ (n - 1) & hash ”戚篙。(n代表數(shù)組長(zhǎng)度)

HashMap的put方法

public class HashMap<K,V> extends AbstractMap<K,V>
    implements Map<K,V>, Cloneable, Serializable {
        static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; //  默認(rèn) 初始化數(shù)組大小 16
        static final int MAXIMUM_CAPACITY = 1 << 30; // 數(shù)組的最大容量 2的30次方
        static final float DEFAULT_LOAD_FACTOR = 0.75f; //擴(kuò)容因子痛倚,加載因子 默認(rèn) 0.75
        
        static final int TREEIFY_THRESHOLD = 8;  //當(dāng)鏈表長(zhǎng)度>=8朋截,轉(zhuǎn)換為紅黑樹 (還有個(gè)前提,數(shù)組長(zhǎng)度>=64)
        
        static final int UNTREEIFY_THRESHOLD = 6; //當(dāng)紅黑樹長(zhǎng)度 小于該值先巴,自動(dòng)轉(zhuǎn)換為鏈表
        
        static final int MIN_TREEIFY_CAPACITY = 64;  //轉(zhuǎn)換為紅黑樹時(shí)其爵,的數(shù)組長(zhǎng)度 要大于該值
        
         transient Node<K,V>[] table;
         
         transient Set<Map.Entry<K,V>> entrySet;
         
         transient int size;
         
         transient int modCount;
         
         int threshold;
          
         final float loadFactor;
         
        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) {
        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)
            tab[i] = newNode(hash, key, value, null);
        else {
            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);
                        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;
    }

        
    }

HashMap 的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;
        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
        }
        else if (oldThr > 0) // initial capacity was placed in threshold
            newCap = oldThr;
        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<K,V>[] newTab = (Node<K,V>[])new Node[newCap];
        table = newTab;
        if (oldTab != null) {
            for (int j = 0; j < oldCap; ++j) {
                Node<K,V> e;
                if ((e = oldTab[j]) != null) {
                    oldTab[j] = null;
                    if (e.next == null)
                        newTab[e.hash & (newCap - 1)] = e;
                    else if (e instanceof TreeNode)
                        ((TreeNode<K,V>)e).split(this, newTab, j, oldCap);
                    else { // preserve order
                        Node<K,V> loHead = null, loTail = null;
                        Node<K,V> hiHead = null, hiTail = null;
                        Node<K,V> next;
                        
                        
  /*
              這里如果判斷成立,那么該元素的地址在新的數(shù)組中就不會(huì)改變伸蚯。
              因?yàn)閛ldCap的最高位的1摩渺,在e.hash對(duì)應(yīng)的位上為0,所以擴(kuò)容后得到的地址是一樣的剂邮,位置不會(huì)改變 摇幻,在后面的代碼的執(zhí)行中會(huì)放到loHead中去,最后賦值給newTab[j]挥萌;
              如果判斷不成立囚企,那么該元素的地址變?yōu)?原下標(biāo)位置+oldCap,也就是lodCap最高位的1瑞眼,在e.hash對(duì)應(yīng)的位置上也為1龙宏,所以擴(kuò)容后的地址改變了,在后面的代碼中會(huì)放到hiHead中伤疙,最后賦值給newTab[j + oldCap]
              舉個(gè)例子來說一下上面的兩種情況:
                設(shè):oldCap=16 二進(jìn)制為:0001 0000
                   oldCap-1=15 二進(jìn)制為:0000 1111
                   e1.hash=10 二進(jìn)制為:0000 1010
                   e2.hash=26 二進(jìn)制為:0101 1010
                e1在擴(kuò)容前的位置為:e1.hash & oldCap-1  結(jié)果為:0000 1010
                e2在擴(kuò)容前的位置為:e2.hash & oldCap-1  結(jié)果為:0000 1010
                結(jié)果相同银酗,所以e1和e2在擴(kuò)容前在同一個(gè)鏈表上,這是擴(kuò)容之前的狀態(tài)徒像。
                現(xiàn)在擴(kuò)容后黍特,需要重新計(jì)算元素的位置,在擴(kuò)容前的鏈表中計(jì)算地址的方式為e.hash & oldCap-1
                那么在擴(kuò)容后應(yīng)該也這么計(jì)算锯蛀,擴(kuò)容后的容量為oldCap*2=32灭衷,2^5, 二進(jìn)制為:0010 0000。 所以 newCap=32旁涤,
                新的計(jì)算方式應(yīng)該為
                e1.hash & newCap-1
                即:0000 1010 & 0001 1111
                結(jié)果為0000 1010與擴(kuò)容前的位置完全一樣翔曲。
                e2.hash & newCap-1 即:0101 1010 & 0001 1111
                結(jié)果為0001 1010,為擴(kuò)容前位置+oldCap。
                而這里卻沒有e.hash & newCap-1 而是 e.hash & oldCap劈愚,其實(shí)這兩個(gè)是等效的瞳遍,都是判斷倒數(shù)第五位是0,還是1菌羽。
                如果是0掠械,則位置不變育八,是1則位置改變?yōu)閿U(kuò)容前位置+oldCap判呕。
                再來分析下loTail, loHead這兩個(gè)的執(zhí)行過程(假設(shè)(e.hash & oldCap) == 0成立):
                第一次執(zhí)行:
                e指向oldTab[j]所指向的node對(duì)象财剖,即e指向該位置上鏈表的第一個(gè)元素.
                loTail為空,所以loHead指向與e相同的node對(duì)象(loHead = e;)隐孽,然后loTail也指向了同一個(gè)node對(duì)象(loTail = e;)。
                最后肚菠,在判斷條件e指向next浸卦,就是指向oldTab鏈表中的第二個(gè)元素
                第二次執(zhí)行:
                lotail不為null,所以lotail.next指向e案糙,這里其實(shí)是lotail指向的node對(duì)象的next指向e,
                也可以說是靴庆,loHead的next指向了e时捌,就是指向了oldTab鏈表中第二個(gè)元素。此時(shí)loHead指向
                的node變成了一個(gè)長(zhǎng)度為2的鏈表炉抒。然后lotail=e也就是指向了鏈表中第二個(gè)元素的地址奢讨。
                第三次執(zhí)行:
                與第二次執(zhí)行類似,loHead上的鏈表長(zhǎng)度變?yōu)?焰薄,又增加了一個(gè)node拿诸,loTail指向新增的node......
                hiTail與hiHead的執(zhí)行過程與以上相同。
                由此可以看出塞茅,loHead是用來保存新鏈表上的頭元素的亩码,loTail是用來保存尾元素的,直到遍歷完鏈表野瘦。
                這是(e.hash & oldCap) == 0成立的時(shí)候描沟。
                (e.hash & oldCap) == 0不成立的情況也相同,其實(shí)就是把oldCap遍歷成兩個(gè)新的鏈表鞭光,
                通過loHead和hiHead來保存鏈表的頭結(jié)點(diǎn)吏廉,然后將兩個(gè)頭結(jié)點(diǎn)放到newTab[j]與newTab[j+oldCap]上面去。
              */
               // 原文鏈接:https://blog.csdn.net/dingjianmin/article/details/109247278
              
              
                        do {
                            next = e.next;
                            if ((e.hash & oldCap) == 0) {
                                if (loTail == null)
                                    loHead = e;
                                else
                                    loTail.next = e; // 相當(dāng)于 loTail指向的Node對(duì)象的next 指向了 e
                                loTail = e; //loTail 位置移動(dòng)到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;
    }
 


HashMap 中的紅黑樹

static final class TreeNode<K,V> extends LinkedHashMap.LinkedHashMapEntry<K,V> {
        TreeNode<K,V> parent;  // red-black tree links
        TreeNode<K,V> left;
        TreeNode<K,V> right;
        TreeNode<K,V> prev;    // needed to unlink next upon deletion
        boolean red;
        TreeNode(int hash, K key, V val, Node<K,V> next) {
            super(hash, key, val, next);
        }

         final TreeNode<K,V> root() {
            for (TreeNode<K,V> r = this, p;;) {
                if ((p = r.parent) == null)
                    return r;
                r = p;
            }
        }

        /**
         * Ensures that the given root is the first node of its bin.
         */
        static <K,V> void moveRootToFront(Node<K,V>[] tab, TreeNode<K,V> root) {}
   
        //找出對(duì)應(yīng)的紅黑樹的節(jié)點(diǎn):查找數(shù)據(jù)
        final TreeNode<K,V> find(int h, Object k, Class<?> kc) {}
       
        final TreeNode<K,V> getTreeNode(int h, Object k) {
           return ((parent != null) ? root() : this).find(h, k, null);
        }
   
        //樹化  Node--》TreeNode
        final void treeify(Node<K,V>[] tab) {}
        //去樹化 TreeNode--》》Node
        final Node<K,V> untreeify(HashMap<K,V> map) {}
        //插入數(shù)據(jù)
        final TreeNode<K,V> putTreeVal(HashMap<K,V> map, Node<K,V>[] tab,
                                       int h, K k, V v) {}
                                       
        //刪除數(shù)據(jù)
        final void removeTreeNode(HashMap<K,V> map, Node<K,V>[] tab,
                                  boolean movable) {}
                                  
        //rehash :擴(kuò)容時(shí)惰许,將舊數(shù)據(jù)rehash席覆,重新確認(rèn)index;將樹分為高區(qū)和低區(qū)兩個(gè)數(shù)
        
        final void split(HashMap<K,V> map, Node<K,V>[] tab, int index, int bit) {}
        
        //左旋
        static <K,V> TreeNode<K,V> rotateLeft(TreeNode<K,V> root,
                                              TreeNode<K,V> p){}
         //右旋
        static <K,V> TreeNode<K,V> rotateRight(TreeNode<K,V> root,  TreeNode<K,V> p){}   
      
         //插入數(shù)據(jù)后,對(duì)紅黑樹結(jié)構(gòu)進(jìn)行自平衡
        static <K,V> TreeNode<K,V> balanceInsertion(TreeNode<K,V> root, TreeNode<K,V> x)
         //刪除數(shù)據(jù)后汹买,對(duì)紅黑樹結(jié)構(gòu)進(jìn)行自平衡
        static <K,V> TreeNode<K,V> balanceDeletion(TreeNode<K,V> root,
                                                   TreeNode<K,V> x)
         //遞歸檢查佩伤,是否符合紅黑樹規(guī)則                                           
        static <K,V> boolean checkInvariants(TreeNode<K,V> t) {}
    
}

參考文檔:https://blog.csdn.net/qq_28063811/article/details/102842862

補(bǔ)充關(guān)于紅黑樹的知識(shí):

參考文檔:http://www.reibang.com/p/e136ec79235c

紅黑樹的特性:

1、每個(gè)節(jié)點(diǎn)要么是紅色晦毙,要么是黑色畦戒,但根節(jié)點(diǎn)永遠(yuǎn)是黑色的;

2结序、每個(gè)紅色節(jié)點(diǎn)的兩個(gè)子節(jié)點(diǎn)一定都是黑色障斋;

3、紅色節(jié)點(diǎn)不能連續(xù)(也即是,紅色節(jié)點(diǎn)的孩子和父親都不能是紅色)垃环;

4邀层、從任一節(jié)點(diǎn)到其子樹中每個(gè)葉子節(jié)點(diǎn)的路徑都包含相同數(shù)量的黑色節(jié)點(diǎn);

5遂庄、所有的葉節(jié)點(diǎn)都是是黑色的(注意這里說葉子節(jié)點(diǎn)其實(shí)是上圖中的 NIL 節(jié)點(diǎn))寥院;
————————————————

原文鏈接:https://blog.csdn.net/javageektech/article/details/102385013

紅黑樹插入情景:

image
image

紅黑樹刪除情景

image

image
  • 紅黑樹:一種自平衡的二叉查找樹
  • 怎么做到自平衡: 左旋,右旋,變色
  • 什么情況下需要自平衡操作: 插入和刪除結(jié)點(diǎn)時(shí)
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市涛目,隨后出現(xiàn)的幾起案子秸谢,更是在濱河造成了極大的恐慌,老刑警劉巖霹肝,帶你破解...
    沈念sama閱讀 218,607評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件估蹄,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡沫换,警方通過查閱死者的電腦和手機(jī)臭蚁,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,239評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來讯赏,“玉大人垮兑,你說我怎么就攤上這事∈妫” “怎么了系枪?”我有些...
    開封第一講書人閱讀 164,960評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)磕谅。 經(jīng)常有香客問我嗤无,道長(zhǎng),這世上最難降的妖魔是什么怜庸? 我笑而不...
    開封第一講書人閱讀 58,750評(píng)論 1 294
  • 正文 為了忘掉前任当犯,我火速辦了婚禮,結(jié)果婚禮上割疾,老公的妹妹穿的比我還像新娘嚎卫。我一直安慰自己,他們只是感情好宏榕,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,764評(píng)論 6 392
  • 文/花漫 我一把揭開白布拓诸。 她就那樣靜靜地躺著,像睡著了一般麻昼。 火紅的嫁衣襯著肌膚如雪奠支。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,604評(píng)論 1 305
  • 那天抚芦,我揣著相機(jī)與錄音倍谜,去河邊找鬼迈螟。 笑死,一個(gè)胖子當(dāng)著我的面吹牛尔崔,可吹牛的內(nèi)容都是我干的答毫。 我是一名探鬼主播,決...
    沈念sama閱讀 40,347評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼季春,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼洗搂!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起载弄,我...
    開封第一講書人閱讀 39,253評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤耘拇,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后宇攻,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體惫叛,經(jīng)...
    沈念sama閱讀 45,702評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,893評(píng)論 3 336
  • 正文 我和宋清朗相戀三年尺碰,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片译隘。...
    茶點(diǎn)故事閱讀 40,015評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡亲桥,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出固耘,到底是詐尸還是另有隱情题篷,我是刑警寧澤,帶...
    沈念sama閱讀 35,734評(píng)論 5 346
  • 正文 年R本政府宣布厅目,位于F島的核電站番枚,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏损敷。R本人自食惡果不足惜葫笼,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,352評(píng)論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望拗馒。 院中可真熱鬧路星,春花似錦、人聲如沸诱桂。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,934評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽挥等。三九已至友绝,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間肝劲,已是汗流浹背迁客。 一陣腳步聲響...
    開封第一講書人閱讀 33,052評(píng)論 1 270
  • 我被黑心中介騙來泰國打工郭宝, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人哲泊。 一個(gè)月前我還...
    沈念sama閱讀 48,216評(píng)論 3 371
  • 正文 我出身青樓剩蟀,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國和親切威。 傳聞我的和親對(duì)象是個(gè)殘疾皇子育特,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,969評(píng)論 2 355

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

  • 1.概述 HashMap是日常java開發(fā)中常用的類之一缰冤,是java設(shè)計(jì)中非常經(jīng)典的一個(gè)類,它巧妙的設(shè)計(jì)思想與實(shí)現(xiàn)...
    Garwer閱讀 2,228評(píng)論 1 28
  • 轉(zhuǎn)載請(qǐng)標(biāo)明出處:http://www.reibang.com/p/22ae6596b004本文出自:【張旭童的簡(jiǎn)書...
    張旭童閱讀 2,721評(píng)論 0 27
  • HashMap工作原理概述 HashMap其實(shí)也是一個(gè)數(shù)組喳魏,只不過每個(gè)元素存儲(chǔ)的是每個(gè)鏈表的頭結(jié)點(diǎn)棉浸。每個(gè)鏈表中每個(gè)...
    九號(hào)鍋爐閱讀 235評(píng)論 0 0
  • 1 概述本文將從幾個(gè)常用方法下手,來閱讀HashMap的源碼刺彩。按照從構(gòu)造方法->常用API(增迷郑、刪、改创倔、查)的順序...
    小小的coder閱讀 180評(píng)論 0 0
  • 一嗡害、簡(jiǎn)介 哈希表也叫散列表,是一種非常重要的數(shù)據(jù)結(jié)構(gòu)畦攘,底層實(shí)現(xiàn)是一個(gè) key - value 鍵值對(duì)霸妹。應(yīng)用場(chǎng)景及其...
    進(jìn)擊的程序猿呀閱讀 130評(píng)論 0 0