HashMap存储结构

<div class="htmledit_views" id="content_views">
                                            <p>本文将通过如下简单的代码来分析HashMap的内部数据结构的变化过程。</p>

<pre class="has" name="code"><code class="language-java hljs"><ol class="hljs-ln"><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="1"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"><span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span><span class="hljs-params">(String[] args)</span> </span>{</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="2"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line">    Map&lt;String, String&gt; map = <span class="hljs-keyword">new</span> HashMap&lt;&gt;();</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="3"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line">    <span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> i = <span class="hljs-number">0</span>; i &lt; <span class="hljs-number">50</span>; i++) {</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="4"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line">        map.put(<span class="hljs-string">"key"</span> + i, <span class="hljs-string">"value"</span> + i);</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="5"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line">    }</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="6"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line">}</div></div></li></ol></code><div class="hljs-button {2}" data-title="复制" οnclick="hljs.copyCode(event)"></div></pre>

<h1><a name="t0"></a>1.数据结构说明</h1>

<p>HashMap中本文需要用到的几个字段如下:</p>

<p><img alt="" class="has" height="152" src="https://img-blog.csdnimg.cn/20181211194804654.png" width="241"></p>

<p>下面说明一下几个字段的含义</p>

<p>1)table</p>

<pre class="has" name="code"><code class="language-java hljs"><ol class="hljs-ln"><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="1"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"><span class="hljs-comment">// HashMap内部使用这个数组存储所有键值对</span></div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="2"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"><span class="hljs-keyword">transient</span> Node&lt;K,V&gt;[] table;</div></div></li></ol></code><div class="hljs-button {2}" data-title="复制" οnclick="hljs.copyCode(event)"></div></pre>

<p>Node的结构如下:</p>

<p><img alt="" class="has" height="126" src="https://img-blog.csdnimg.cn/20181211194852435.png" width="200"></p>

<p>可以发现,Node其实是一个链表,通过next指向下一个元素。</p>

<p>&nbsp;</p>

<p>2)size</p>

<p>记录了HashMap中键值对的数量</p>

<p>&nbsp;</p>

<p>3)modCount</p>

<p>记录了HashMap在结构上更改的次数,包括可以更改键值对数量的操作,例如put、remove,还有可以修改内部结构的操作,例如rehash。</p>

<p>&nbsp;</p>

<p>4)threshold</p>

<p>记录一个临界值,当已存储键值对的个数大于这个临界值时,需要扩容。</p>

<p>&nbsp;</p>

<p>5)loadFactor</p>

<p>负载因子,通常用于计算threshold,threshold=总容量*loadFactor。</p>

<h1><a name="t1"></a>2.new HashMap</h1>

<p>new HashMap的源码如下:</p>

<pre class="has" name="code"><code class="language-java hljs"><ol class="hljs-ln"><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="1"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"><span class="hljs-comment"><span class="hljs-comment">/**</span></span></div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="2"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"><span class="hljs-comment">* The load factor used when none specified in constructor.</span></div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="3"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"><span class="hljs-comment">* 负载因子,当 已使用容量 &gt; 总容量 * 负载因子 时,需要扩容</span></div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="4"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"><span class="hljs-comment">*/</span></div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="5"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"><span class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> <span class="hljs-keyword">float</span> DEFAULT_LOAD_FACTOR = <span class="hljs-number">0.75f</span>;</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="6"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> </div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="7"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"><span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">HashMap</span><span class="hljs-params">()</span> </span>{</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="8"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line">    <span class="hljs-keyword">this</span>.loadFactor = DEFAULT_LOAD_FACTOR; <span class="hljs-comment">// all other fields defaulted</span></div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="9"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line">}</div></div></li></ol></code><div class="hljs-button {2}" data-title="复制" οnclick="hljs.copyCode(event)"></div></pre>

<p>此时,HashMap只初始化了负载因子(使用默认值0.75),并没有初始化table数组。</p>

<p>其实HashMap使用的是延迟初始化策略,当第一次put的时候,才初始化table(此时table是null)。</p>

<h1><a name="t2"></a>3.table数组的初始化</h1>

<p>当第一次put的时候,HashMap会判断当前table是否为空,如果是空,会调用resize方法进行初始化。</p>

<p>resize方法会初始化一个容量大小为16 的数组,并赋值给table。</p>

<p>并计算threshold=16*0.75=12。</p>

<p>此时table数组的状态如下:</p>

<p><img alt="" class="has" height="184" src="https://img-blog.csdnimg.cn/20181211195001242.png" width="1105"></p>

<h1><a name="t3"></a>4.put过程</h1>

<pre class="has" name="code"><code class="language-java hljs">map.put(<span class="hljs-string">"key0"</span>, <span class="hljs-string">"value0"</span>);</code><div class="hljs-button {2}" data-title="复制" οnclick="hljs.copyCode(event)"></div></pre>

<p>首先计算key的hash值,hash("key0") = 3288451</p>

<p>计算这次put要存入数组位置的索引值:index=(数组大小 - 1) &amp; hash = 3</p>

<p>判断 if (table[index] == null)&nbsp;就new一个Node放到这里,此时为null,所以直接new Node放到3上,此时table如下:</p>

<p><img alt="" class="has" height="184" src="https://img-blog.csdnimg.cn/20181211195042802.png" width="1081"></p>

<p>然后判断当前已使用容量大小(size)是否已经超过临界值threshold,此时size=1,小于12,不做任何操作,put方法结束(如果超过临界值,需要resize扩容)。</p>

<p>&nbsp;</p>

<p>继续put。。。</p>

<pre class="has" name="code"><code class="language-java hljs">map.put(<span class="hljs-string">"key1"</span>, <span class="hljs-string">"value1"</span>);</code><div class="hljs-button {2}" data-title="复制" οnclick="hljs.copyCode(event)"></div></pre>

<p><img alt="" class="has" height="189" src="https://img-blog.csdnimg.cn/20181211195110501.png" width="1130"></p>

<pre class="has" name="code"><code class="language-java hljs"><ol class="hljs-ln"><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="1"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line">map.put(<span class="hljs-string">"key1"</span>, <span class="hljs-string">"value1"</span>);</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="2"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line">map.put(<span class="hljs-string">"key2"</span>, <span class="hljs-string">"value2"</span>);</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="3"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line">map.put(<span class="hljs-string">"key3"</span>, <span class="hljs-string">"value3"</span>);</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="4"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line">map.put(<span class="hljs-string">"key4"</span>, <span class="hljs-string">"value4"</span>);</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="5"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line">map.put(<span class="hljs-string">"key5"</span>, <span class="hljs-string">"value5"</span>);</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="6"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line">map.put(<span class="hljs-string">"key6"</span>, <span class="hljs-string">"value6"</span>);</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="7"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line">map.put(<span class="hljs-string">"key8"</span>, <span class="hljs-string">"value7"</span>);</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="8"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line">map.put(<span class="hljs-string">"key9"</span>, <span class="hljs-string">"value9"</span>);</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="9"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line">map.put(<span class="hljs-string">"key10"</span>, <span class="hljs-string">"value10"</span>);</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="10"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line">map.put(<span class="hljs-string">"key11"</span>, <span class="hljs-string">"value11"</span>);</div></div></li></ol></code><div class="hljs-button {2}" data-title="复制" οnclick="hljs.copyCode(event)"></div></pre>

<p><img alt="" class="has" height="190" src="https://img-blog.csdnimg.cn/20181211195128234.png" width="1200"></p>

<p>此时size=12,下一次put后size为13,大于当前threshold,将触发扩容(resize)</p>

<pre class="has" name="code"><code class="language-java hljs">map.put(<span class="hljs-string">"key12"</span>, <span class="hljs-string">"value12"</span>);</code><div class="hljs-button {2}" data-title="复制" οnclick="hljs.copyCode(event)"></div></pre>

<p>计算Key的hash值,hash("key12")=101945043,计算要存入table位置的索引值 = (总大小 - 1) &amp; hash = (16 - 1) &amp;&nbsp;101945043 = 3</p>

<p>从目前的table状态可知,table[3] != null,但此时要put的key与table[3].key不相等,我们必须要把他存进去,此时就产生了哈希冲突(哈希碰撞)。</p>

<p>&nbsp;</p>

<p>这时链表就派上用场了,HashMap就是通过链表解决哈希冲突的。</p>

<p>HashMap会创建一个新的Node,并放到table[3]链表的最后面。</p>

<p>此时table状态如下:</p>

<p><img alt="" class="has" height="309" src="https://img-blog.csdnimg.cn/20181211195224342.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3h6bV9yYWluYm93,size_16,color_FFFFFF,t_70" width="1200"></p>

<h1><a name="t4"></a>5.resize扩容</h1>

<p>此时table中一共有13个元素,已经超过了threshold(12),需要对table调用resize方法扩容。</p>

<p>HashMap会创建一个容量为之前两倍(16*2=32)的table,并将旧的Node复制到新的table中,新的临界值(threshold)为24(32*0.75)。</p>

<p>下面主要介绍一下复制的过程(并不是原封不动的复制,Node的位置可能发生变化)</p>

<p>先来看源码:</p>

<pre class="has" name="code"><code class="language-java hljs"><ol class="hljs-ln"><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="1"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"><span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> j = <span class="hljs-number">0</span>; j &lt; oldCap; ++j) { <span class="hljs-comment">// oldCap:旧table的大小 =16</span></div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="2"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line">    Node&lt;K,V&gt; e;</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="3"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line">    <span class="hljs-keyword">if</span> ((e = oldTab[j]) != <span class="hljs-keyword">null</span>) { <span class="hljs-comment">// oldTab:旧table的备份</span></div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="4"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line">        oldTab[j] = <span class="hljs-keyword">null</span>;</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="5"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line">        <span class="hljs-comment">// 如果数组中的元素没有后继节点,直接计算新的索引值,并将Node放到新数组中</span></div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="6"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line">        <span class="hljs-keyword">if</span> (e.next == <span class="hljs-keyword">null</span>)</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="7"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line">            newTab[e.hash &amp; (newCap - <span class="hljs-number">1</span>)] = e;</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="8"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line">        <span class="hljs-comment">// 忽略这个else if。其实,如果链表的长度超过8,HashMap会把这个链表变成一个树结构,树结构中的元素是TreeNode</span></div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="9"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line">        <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (e <span class="hljs-keyword">instanceof</span> TreeNode)</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="10"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line">            ((TreeNode&lt;K,V&gt;)e).split(<span class="hljs-keyword">this</span>, newTab, j, oldCap);</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="11"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line">        <span class="hljs-comment">// 有后继节点的情况</span></div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="12"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line">        <span class="hljs-keyword">else</span> { <span class="hljs-comment">// preserve order</span></div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="13"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line">            Node&lt;K,V&gt; loHead = <span class="hljs-keyword">null</span>, loTail = <span class="hljs-keyword">null</span>;</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="14"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line">            Node&lt;K,V&gt; hiHead = <span class="hljs-keyword">null</span>, hiTail = <span class="hljs-keyword">null</span>;</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="15"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line">            Node&lt;K,V&gt; next;</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="16"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line">            <span class="hljs-keyword">do</span> {</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="17"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line">                next = e.next;</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="18"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line">                <span class="hljs-comment">// 【说明1】</span></div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="19"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line">                <span class="hljs-keyword">if</span> ((e.hash &amp; oldCap) == <span class="hljs-number">0</span>) {</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="20"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line">                    <span class="hljs-keyword">if</span> (loTail == <span class="hljs-keyword">null</span>)</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="21"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line">                        loHead = e;</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="22"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line">                    <span class="hljs-keyword">else</span></div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="23"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line">                        loTail.next = e;</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="24"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line">                    loTail = e;</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="25"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line">                }</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="26"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line">                <span class="hljs-keyword">else</span> {</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="27"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line">                    <span class="hljs-keyword">if</span> (hiTail == <span class="hljs-keyword">null</span>)</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="28"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line">                        hiHead = e;</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="29"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line">                    <span class="hljs-keyword">else</span></div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="30"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line">                        hiTail.next = e;</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="31"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line">                    hiTail = e;</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="32"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line">                }</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="33"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line">            } <span class="hljs-keyword">while</span> ((e = next) != <span class="hljs-keyword">null</span>);</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="34"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line">            <span class="hljs-keyword">if</span> (loTail != <span class="hljs-keyword">null</span>) {</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="35"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line">                loTail.next = <span class="hljs-keyword">null</span>;</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="36"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line">                newTab[j] = loHead;</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="37"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line">            }</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="38"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line">            <span class="hljs-keyword">if</span> (hiTail != <span class="hljs-keyword">null</span>) {</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="39"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line">                hiTail.next = <span class="hljs-keyword">null</span>;</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="40"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line">                <span class="hljs-comment">//【说明2】</span></div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="41"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line">                newTab[j + oldCap] = hiHead;</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="42"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line">            }</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="43"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line">        }</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="44"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line">    }</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="45"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line">}</div></div></li></ol></code><div class="hljs-button {2}" data-title="复制" οnclick="hljs.copyCode(event)"></div></pre>

<p>【说明1】遍历链表,计算链表每一个节点在新table中的位置。</p>

<p>计算位置的方式如下:</p>

<p>1)如果节点的 (hash &amp; oldCap) == 0,那么该节点还在原来的位置上,为什么呢?</p>

<p>因为oldCap=16,二进制的表现形式为0001 0000,任何数&amp;16,如果等于0,那么这个数的第五个二进制位必然为0。</p>

<p>以当前状态来说,新的容量是32,那么table的最大index是31,31的二进制表现形式是00011111。</p>

<p>计算index的方式是 hash &amp; (容量 - 1),也就是说,新index的计算方式为 hash &amp; (32 - 1)</p>

<p>假设Node的hash = 01101011,那么</p>

<p><img alt="" class="has" height="210" src="https://img-blog.csdnimg.cn/20181211210658476.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3h6bV9yYWluYm93,size_16,color_FFFFFF,t_70" width="462"></p>

<p>&nbsp;</p>

<p>2)下面再对比(hash &amp; oldCap) != 0的情况</p>

<p>如果节点的(hash &amp; oldCap) != 0,那么该节点的位置=旧index + 旧容量大小</p>

<p>假设Node的hash = 01111011,那么</p>

<p><img alt="" class="has" height="196" src="https://img-blog.csdnimg.cn/20181211210723662.png" width="460"></p>

<p>上一个例子的hash值01101011跟这个例子的hash值01111011只是在第5位二进制上不同,可以发现,这两个值在旧的table中,是在同一个index中的,如下:</p>

<p><img alt="" class="has" height="196" src="https://img-blog.csdnimg.cn/20181211210744375.png" width="446"></p>

<p><img alt="" class="has" height="198" src="https://img-blog.csdnimg.cn/20181211210801275.png" width="441"></p>

<p>由于扩容总是以2倍的方式进行,也就是:旧容量 &lt;&lt; 1,这也就解释了【说明2】,当(hash &amp; oldCap) != 0时,这个Node的新index = 旧index + 旧容量大小。</p>

<p>扩容后,table状态如下所示:</p>

<p><img alt="" class="has" height="319" src="https://img-blog.csdnimg.cn/20181211210838961.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3h6bV9yYWluYm93,size_16,color_FFFFFF,t_70" width="1200"></p>

<p>&nbsp;</p>

<p>最终,重新分配完所有的Node后,扩容结束。</p>

<hr><p>大家可以关注本人公众号【Mr羽墨青衫】,会定期推送一些原创技术文章。</p>

<p style="text-align:center;"><img alt="公众号" class="has" height="258" src="https://img-blog.csdnimg.cn/20181211195946208.jpg" width="258"></p>

<p>&nbsp;</p>                                    </div>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值