JDK1.8 source of the HashMap (II) - and inserted into the expansion

  Understanding the principles underlying implementation HashMap, can be easily deduced step HashMap insertion element, the element is calculated first hash value, mod the length of the hash table index is then obtained, the last to be stored in fob tub, look at the source.

public V PUT (K Key, V value) {
         return PutVal (the hash (Key), Key, value, to false , to true ); 
    } 

    Final V PutVal ( int the hash, K Key, V value, Boolean onlyIfAbsent ,
                    Boolean The evict) { 
        the Node <K, V> [] Tab; the Node <K, V> P; int n-, I; 
      // hash table is empty, or a length of 0, initialize
IF ((Tab = table) == null || (= n-tab.length) == 0 ) n- = (Tab = a resize ()) length;.
      tub // do not contain key-value pairs located directly on the reference key into the tub
if((P = Tab [I = (n--. 1) & the hash]) == null ) Tab [I] = the newNode (the hash, Key, value, null ); the else {
        // node of the insertion node is stored in e-REFERENCE the Node
<K, V> e; K K;
        // if the key values and the hash value and the bucket equal to the first key, the key point of the e
IF (p.hash the hash == && (( K = p.key) == || Key (Key =! null && key.equals (K)))) E = P;
        // node type if the bucket is tree-node, using red-black tree insert mode
the else IF (P the instanceof the TreeNode) E = ((the TreeNode <K, V>) P) .putTreeVal (the this , Tab, the hash, Key, value); the else {
          // traverse the list
for ( int BinCount = 0;; ++ BinCount) {
            // list traversal is not found in the end still chain contains the same key to be inserted the node key to be inserted will be the last node in the linked list hanging
IF ((E = p.next) == null ) { p.next = the newNode (the hash, Key, value, null );
              // if linked to the new node bucket list length after the chain is greater than the threshold value of the tree for tree list operation
IF (BinCount> = TREEIFY_THRESHOLD -. 1) // -1 for 1st treeifyBin (Tab, the hash); BREAK ; }
            // When the list contains the current key to be inserted, terminating traversal
IF (e.hash the hash == && ((K = e.key) == || Key (Key! = Null && key.equals (K))) ) BREAK ; P = E; } }
        // E! key = null, indicates the presence of the linked list to be inserted in the tub node
IF (E =! null ) { // existing Key Mapping for V = oldValue e.Value;
          // onlyIfAbsent only indicates whether there is empty oldValue updated key value pairs
IF (! onlyIfAbsent oldValue || == null ) e.Value =value; afterNodeAccess (E); return oldValue; } } ++ ModCount;
     // number of key-value exceeds a threshold, expansion operation
IF (size ++> threshold) a resize (); afterNodeInsertion (The evict); return null ; }

   HashMap insert elements I have resolved the major steps with explanatory notes, should not be difficult to understand, here would like to say about that onlyIfAbsent this variable, this variable is represented: when the bucket list exists to insert the key nodes, if only updating the value of key-value pairs in the case of the old value of nULL, this method can be seen put transmission parameter value is false, so HashMap upon insertion node, if the same key, the new value replaces the old value.

  When PS. In the HashMap, when used as a custom class type bond, if the custom class overrides the equals method, must override the hashCode method can be seen in the source code, the HashMap key equality comparison, first Comparative key hash values, then use equals, if not override hashCode method overrides the equals method, two objects are actually occurs "equal", but not to be considered in the HashMap two identical key , resulting in the same key value of the node values ​​are not updated when a node is inserted, but is regarded as a new node, or can not find the key to find the appropriate value based on the value of the operation. Overall, key value comparison is mainly the following two points:

  1, if two objects are the same (i.e., equals with comparative returns true), then their value must be the same hashCode.
  2, if two objects have the same hashCode, they are not necessarily the same (i.e., equals with comparative return false).

 

  Next step is to talk about Hash Map of the expansion mechanism. HashMap expansion divided into two steps, the first step will be extended to twice the length of the barrel, the second step calculating a hash value, re-fob.

  Source follows, a bit long.

Final the Node <K, V> [] a resize () { 
        the Node <K, V> [] = oldTab Table;
         int OLDCAP = (oldTab == null ) 0? : oldTab.length;
         int oldThr = threshold;
         int newCap, newThr 0 = ; 
     // Table is not empty, indicating already been initialized
IF (OLDCAP> 0 ) {
       // has reached its maximum capacity, no expansion, the threshold value to the maximum
IF (OLDCAP> = MAXIMUM_CAPACITY) { threshold = Integer. MAX_VALUE; return oldTab; }
       // calculated by new capacity and twice the old threshold value and the threshold capacity value
else IF ((newCap = OLDCAP <<. 1) <MAXIMUM_CAPACITY && OLDCAP > = DEFAULT_INITIAL_CAPACITY) newThr = oldThr <<. 1; // Double threshold }
     // Table has not been initialized,将threshold 的值赋值给 newCap
the else IF (oldThr> 0) // Initial Capacity WAS Placed threshold in newCap = oldThr;
     // Table is not initialized, the default capacity set capacity, the capacity and the load factor threshold is a product of
the else { // ZERO Initial threshold signifies the using defaults newCap = DEFAULT_INITIAL_CAPACITY; newThr = ( int) (* DEFAULT_LOAD_FACTOR DEFAULT_INITIAL_CAPACITY); }
     // re-calculate the threshold according to the equation
IF (newThr == 0 ) { a float . Ft = ( a float ) newCap * loadFactor; newThr = (newCap <&& MAXIMUM_CAPACITY. Ft <( a float )? MAXIMUM_CAPACITY ( int ) . ft: Integer.MAX_VALUE); } threshold = newThr; @SuppressWarnings ({ "rawtypes", "an unchecked" })
       // Create a new bucket array, to complete the initialization the Node
<K, V> [] = newtab (the Node <K, V>[])new the Node [newCap]; table = newtab;
     // old hash table is not empty, re fob
IF (oldTab =! null ) {
       // iterate tub
for ( int J = 0; J <OLDCAP; ++ J) { the Node <K, V> E;
          the presence of key-value pair // tub
IF ((E = oldTab [J]) =! null ) { oldTab [J] = null ;
            // tub there is only one key for calculating a hash value put table into the new hash bucket
IF (e.next == null ) newtab [e.hash & (newCap -. 1)] = E;
            // If the node is a tree, red-black tree to split
the else IF (E the instanceof the TreeNode) ((the TreeNode <K, V>) E) .split ( the this , newtab, J, OLDCAP);
            // traverse the list, the order of the original packet
the else { // the preserve Order the Node <K, V> = loHead null , loTail = null ; the Node <K, V> hiHead = null , hiTail = null ; the Node <K, V> Next; do { Next = e.next; 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; } } the while (! (E = Next) = null );
              // mapped to the new tub
IF (loTail =! null ) { loTail.next = null ; newtab [J] = loHead ; } IF (! hiTail = null ) { hiTail.next = null ; newtab [j + oldCap] =hiHead; } } } } } Return newtab; }

   We step by step to resolve this:

  1) Calculate the new size and a new threshold

  Whole calculation process corresponding to the above first source and a second conditional branch, as follows:

// first conditional branch
IF
(OLDCAP> 0 ) {
   // branch nested
IF (OLDCAP> = MAXIMUM_CAPACITY) {...} the else IF ((newCap = OLDCAP <<. 1) <MAXIMUM_CAPACITY && OLDCAP > = DEFAULT_INITIAL_CAPACITY) ...} {
}
the else IF (oldThr> 0) { ...} the else {...}
// second conditional branch
IF (newThr == 0 ) {...}

  The first conditional branch coverage as follows:

  •  oldCap> 0: table has been initialized.
  •  oldCap == 0 && oldThr> 0: table has not been initialized and the threshold> 0, calls HashMap (int) and HashMap (int, float) construction method produces such a case, newCap this case = threshold = tableSizeFor (initialCapacity ) , the new threshold value calculated in the second branch condition.
  • oldCap == 0 && oldThr == 0: table has not been initialized and the threshold = 0, call the HashMap () constructor method of producing such a case, the capacity of this first set to default values, the threshold value is calculated by the equation.

    The second condition branch coverage as follows:

  • newThr == 0: first a new conditional branch new threshold or thresholds zeroing overflow during the calculation, is calculated according to the formula uncalculated threshold.

  The above is the new capacity calculation process and the new threshold value.

 

  2) compute the hash nodes, re lanyards

  In Java8, necessary to determine remapped node type if the node is a tree, the remapper needs to split them, if the list type node, the first packet will have its remapper, this article only list of list node mapping analysis.

  Subscripts barrel calculated as (n-- . 1 ) & the hash , the hash table is assumed that the old capacity of 16, two elements of different hash values, but the calculation with n-1, since only four bits involved in computing, to give the same indices of the tub.

  After expansion capacity becomes hash table 32, the resulting index changes in the tub, as shown in FIG.

 

  After the expansion, the number of bits involved in the operation becomes a five, fifth place due to the difference between the two hash values, the resulting index is also not the same as the barrel, so that the list of types of nodes that the new grouping separate nodes in the same node position of the home position, a chain split into two buckets, and ensure that the original order. By HashMap (E. The hash & OLDCAP ) == 0 is determined whether the node is a new node position, as shown in FIG.

 

  Expansion methods use the pointer to the head node and the end node loHead, loTail, hiHead, hiTail four two storage nodes of the list.

  loHead: original position of the node pointer list head node 

  loTail: original position of the node at the end of the linked list node pointer

  hiHead: The new head node pointer location node list

  hiTail: the new position at the end of the linked list node pointer node 

  Finally, the two corresponding to the list stored in the tub.

 

  These are the source HashMap insert key-value pairs as well as the expansion part of the resolution.

  References: https: //www.cnblogs.com/chn58/p/6544599.html

       https://segmentfault.com/a/1190000012926722

 

Guess you like

Origin www.cnblogs.com/youtang/p/11257494.html