<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          HashMap部分源碼集解

          共 7374字,需瀏覽 15分鐘

           ·

          2023-06-05 01:45

          ????????本文將主要分析HashMap的重點方法,包括put(), get(), resize()。并對其重點代碼進行解釋說明。

                
                  
                       #
                    # 閱讀本文需具備基礎的知識:
                  
                
                
                  ??? 1. 了解Java基礎知識;
                
                
                  ??? 2. 對HashMap有一定的了解并懂得HashMap中一些基本屬性的作用;
                
              





          ##?put?方法集解

                
                  public V put(K key, V value) {
                
                
                      //計算key的hash值
                
                
                      return putVal(hash(key), key, value, false, true);
                
                
                  }
                
                
                  
                    

          final V putVal(int hash, K key, V value, boolean onlyIfAbsent, boolean evict) { Node[] tab; Node p; int n, i; // Node數(shù)組非空判斷,為空則調用resize方法進行初始化 if ((tab = table) == null || (n = tab.length) == 0) n = (tab = resize()).length; // 通過(n - 1) & hash 的方式計算出key的位置,再對當前位置的元素進行非空判斷,為null則直接插入 if ((p = tab[i = (n - 1) & hash]) == null) ????????tab[i]?=?newNode(hash,?key,?value,?null); else { Node e; K k; //判斷key是否存在,存在則下面邏輯選擇性覆蓋舊值 if (p.hash == hash && ((k = p.key) == key || (key != null && key.equals(k)))) e = p; //判斷是否為紅黑樹結構,是的話以紅黑樹的方式插入 else if (p instanceof TreeNode) e = ((TreeNode)p).putTreeVal(this, tab, hash, key, value); else { for (int binCount = 0; ; ++binCount) { // 鏈表的下一節(jié)點為null,將數(shù)據(jù)插入尾部 if ((e = p.next) == null) { p.next = newNode(hash, key, value, null); //鏈表長度 >= 8 則轉變?yōu)榧t黑樹 if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st treeifyBin(tab, hash); break; } //如果鏈表中存在key值相等元素則跳出循環(huán),在下面邏輯選擇性覆蓋舊值 if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k)))) break; p = e; } } // 如果key已存在,則根據(jù)onlyIfAbsent選擇性用新值替換舊值 if (e != null) { // existing mapping for key V oldValue = e.value; if (!onlyIfAbsent || oldValue == null) e.value = value; afterNodeAccess(e); return oldValue; } } // HashMap線程安全的保障 ++modCount; // 當Node數(shù)組的長度大于擴容閾值時則進行擴容 if (++size > threshold) resize(); afterNodeInsertion(evict); return null; }

          ##?put重點代碼發(fā)明

          1.??為什么用(n - 1) & hash 的方式計算元素下標?

                
                  原因:(n - 1)?& hash == hash % n, 并且(n - 1)?& hash 的效率要高。(n為2的次冪)
                
                
                  解釋:
                
                
                    當n == 8 == 1000, hash == 59 == 111011時。
                
                
                  ?  (n?-?1)?&?hash?==?11?并且?111011?==?11?==?3,
                
                
                  ???59?%?8?==?3
                
                
                  ??根據(jù)位運算規(guī)則可知,二進制數(shù)中凡是低三位為0的數(shù)都可以被100整除,低三位的值即為余數(shù);
                
                
                    利用位與運算,將高位置0,低三位置為原值,結果低三位的原值即為余數(shù)。
                
                
                    所以當n為2的次冪時,(n?-?1)?&?hash?即為?hash?%?n的值
                
                
              

          2. putVal方法中onlyIfAbsent的意義及作用

          onlyIfAbsent: ???? ????- 如果存在新key和舊key相同,是否用新值覆蓋舊值;???? ????- 如果為true,則新值不覆蓋舊值。false則進行覆蓋。

          3. modCount的作用

          記錄集合被修改的次數(shù)。 當操作HashMap時,發(fā)現(xiàn)modCount發(fā)生改變時進入快速失敗,拋出 ConcurrentModificati onException





          ##?resize?方法集解

                
                  final Node[] resize() {
                
                
                      Node[] oldTab = table;
                
                
                      // 獲取原哈希表容量;若原哈希表為null,則容量為0
                
                
                      int oldCap = (oldTab == null) ? 0 : oldTab.length;
                
                
                      // 獲取原哈希表擴容門檻
                
                
                      int oldThr = threshold;
                
                
                      // 初始化新哈希表容量和新哈希表門檻為0
                
                
                      int newCap, newThr = 0;
                
                
                      // 如果原容量大于0,計算擴容后的容量及新的負載擴容門檻
                
                
                      if (oldCap > 0) {
                
                
                          //判斷原容量是否大于等于HashMap允許的容量最大值 - 2的30次冪
                
                
                          if (oldCap >= MAXIMUM_CAPACITY) {
                
                
                              // 如果原容量大于等于允許的最大容量,則把HashMap的擴容門檻設置為Integer允許的最大值
                
                
                              threshold = Integer.MAX_VALUE;
                
                
                              //不再擴容,直接返回
                
                
                              return oldTab;
                
                
                          }
                
                
                          // 把原Node數(shù)組容量擴大為原來的2倍作為新Node數(shù)組的容量。
                
                
                          // 如果新Node數(shù)組的容量小于HashMap所允許的容量的最大值: 2的30次冪,并且原Node數(shù)組容量大于等于默認的初始化Node數(shù)組容量: 2的4次冪 -> 16,則計算新的擴容門檻
                
                
                          else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY && oldCap >= DEFAULT_INITIAL_CAPACITY)
                
                
                              // 新Node數(shù)組的擴容門檻為原Node數(shù)組的擴容門檻的2倍
                
                
                              newThr = oldThr << 1; // double threshold
                
                
                      }
                
                
                      else if (oldThr > 0) // initial capacity was placed in threshold
                
                
                          // 如果原Node數(shù)組容量小于等于0并且原Node數(shù)組擴容門檻大于0,新Node數(shù)組容量為原Node數(shù)組擴容門檻大小
                
                
                          newCap = oldThr;
                
                
                      else {               // zero initial threshold signifies using defaults
                
                
                          // 如果原Node數(shù)組容量小于等于0并且原Node數(shù)組的擴容門檻小于等于0,則初始化新Node數(shù)組容量為默認初始化容量:1 << 4;
                
                
                          newCap = DEFAULT_INITIAL_CAPACITY;
                
                
                          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 = newThr;
                
                
                      @SuppressWarnings({"rawtypes","unchecked"})
                
                
                      //初始化新NodeNode數(shù)組
                
                
                      Node[] newTab = (Node[])new Node[newCap];
                
                
                      table = newTab;
                
                
                      //舊Node數(shù)組不為null,則進行擴容
                
                
                      if (oldTab != null) {
                
                
                          for (int j = 0; j < oldCap; ++j) {
                
                
                              Node e;
                
                
                              if ((e = oldTab[j]) != null) {
                
                
                                  oldTab[j] = null;
                
                
                                  if (e.next == null)
                
                
                                      // 計算新Map的Node數(shù)組元素下標。如果當前Node節(jié)點沒有后續(xù)節(jié)點,則直接放入新位置
                
                
                                      newTab[e.hash & (newCap - 1)] = e;
                
                
                                  // 如果當前Node是紅黑樹,則對樹進行處理
                
                
                                  else if (e instanceof TreeNode)
                
                
                                      ((TreeNode)e).split(this, newTab, j, oldCap);
                
                
                                  // 處理鏈表
                
                
                                  else { // preserve order
                
                
                                      Node loHead = null, loTail = null;
                
                
                                      Node hiHead = null, hiTail = null;
                
                
                                      Node next;
                
                
                                      do {
                
                
                                          next = e.next;
                
                
                                          // (e.hash & oldCap) == 0 元素放在低位區(qū),否則放在高位區(qū)
                
                
                                          // 低位區(qū):原Node長度的位置;高位區(qū):原Node長度到擴容后Node長度的位置
                
                
                                          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);
                
                
                                      // 將低位區(qū)的尾結點指向null,并把低位區(qū)的頭結點放入新Node數(shù)組的合適位置
                
                
                                      if (loTail != null) {
                
                
                                          loTail.next = null;
                
                
                                          newTab[j] = loHead;
                
                
                                      }
                
                
                                      // 將高位區(qū)的頭結點放到新Node數(shù)組合適的位置
                
                
                                      if (hiTail != null) {
                
                
                                          hiTail.next = null;
                
                
                                          newTab[j + oldCap] = hiHead;
                
                
                                      }
                
                
                                  }
                
                
                              }
                
                
                          }
                
                
                      }
                
                
                      return newTab;
                
                
                      }
                
              

          ##?resize重點代碼發(fā)明

          1. ?為什么要進行擴容?


          ????????減少hash碰撞,使HashMap中的結構更簡單。

          2. 為什么要以2的倍數(shù)作為擴容的策略?

          ????????保證HashMap的容量都是2的次冪,便于key的位置計算和擴容的容量計算。




          ## get方法集解

                
                  public V get(Object key) {
                
                
                      Node e;
                
                
                      //獲取key的hash值
                
                
                      return (e = getNode(hash(key), key)) == null ? null : e.value;
                
                
                  }
                
                
                  
                    
          final Node getNode(int hash, Object key) { Node[] tab; Node first, e; int n; K k; //Node數(shù)組不為null并且其長度大于0并且key位置部位null,則獲取當前key位置的Node,否則返回null if ((tab = table) != null && (n = tab.length) > 0 && (first = tab[(n - 1) & hash]) != null) { //當前位置的key和入?yún)ey相等,則返回當前Node if (first.hash == hash && ((k = first.key) == key || (key != null && key.equals(k)))) return first; // 如果首Node節(jié)點為空,直接返回null。否則去鏈表或紅黑樹中取值 if ((e = first.next) != null) { //判斷是否是紅黑樹,是的話在樹中取值 if (first instanceof TreeNode) return ((TreeNode)first).getTreeNode(hash, key); do { ????????????????//在鏈表中取值,如果key值相等,則取鏈表中的當前Node節(jié)點 if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k)))) return e; } while ((e = e.next) != null); } } //返回null return null; }



          疑問:

          現(xiàn)狀:在resize方法中,代碼 hiTail.next = null;?表示將高位區(qū)的尾結點的next指向null。

          問:"這個操作不能將高位區(qū)和低位區(qū)的Node節(jié)點通過指針相連,那么遍歷鏈表時如何遍歷到低位區(qū)的數(shù)據(jù)?為什么不將高位區(qū)尾節(jié)點的next指向低位區(qū)的頭指針?"


          注:

          集解 -?引自《本草綱目》,意為收集業(yè)內(nèi)眾家釋義;

          發(fā)明 -?引自《本草綱目》,意為筆者獨立的見解,有別于他。



          瀏覽 34
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  亚洲高清无码在线观看视频 | 色五月婷婷中文字幕 | 国产亚洲精品久久久久动 | 亚洲高清无码视频下载 | v11av |