Java class set Framework HashMap (JDK1.8) Source code Analysis

Source: Internet
Author: User


These days learned the bottom of the HashMap implementation, found about HASHMAP implementation of the blog is still a lot of, but almost all JDK1.6 version, and my JDK version is 1.8.0_25, in contrast, found HASHMAP implementation changes. This blog has been on and off for a day, understand the inappropriate, welcome to correct.

In JDK1.6, HashMap uses a bit-bucket + linked list implementation, that is, the use of linked list processing conflicts, the same hash value of the linked list is stored in a list. However, when there are more elements in a bucket, that is, if the hash value is more than one element, it is inefficient to find the key value in turn. In the JDK1.8, the HashMap uses the bit bucket + linked list + red black tree implementation, when the list length exceeds the threshold (8), the link list is converted to the red black tree, this greatly reduces the search time.

The following code is directly affixed:

1 Data structures involved: dealing with hash conflicts linked lists and red and black trees and bit barrels

    Node is a one-way list that implements the Map.entry interface static class Node<k,v> implements map.entry<k,v> {final int hash;        Final K Key;        V value;        Node<k,v> Next;            Constructor hash value key value next node (int hash, K key, V value, node<k,v> next) {This.hash = hash;            This.key = key;            This.value = value;        This.next = Next;        } Public final K GetKey () {return key;}        Public final V GetValue () {return value;}        Public final String toString () {return key + "=" + value;}        Public final int hashcode () {return Objects.hashcode (key) ^ Objects.hashcode (value);            Public final V SetValue (v newvalue) {v oldValue = value;            value = newvalue;        return oldValue; }//Determine if two node is equal and returns true if both key and value are equal.         Can compare itself to true public final Boolean equals (Object o) {if (o = = this) return true;   if (o instanceof map.entry) {map.entry<?,? > E = (map.entry<?,? >) o;                    if (Objects.equals (Key, E.getkey ()) && objects.equals (value, E.getvalue ()))            return true;        } return false; }    }

    Red and Black tree    static final class Treenode<k,v> extends linkedhashmap.entry<k,v> {        treenode<k,v> parent;  Parent node        treenode<k,v> left;//left dial hand tree        treenode<k,v> right;//right subtree        treenode<k,v> prev;    needed to unlink next upon deletion        boolean red;//color attribute        TreeNode (int hash, K key, V Val, node<k,v> next) {            super (hash, Key, Val, next);        }        Returns the root node of the current node        final treenode<k,v> root () {for            (treenode<k,v> r = this, p;;) {                if (p = r.parent) = = null)                    return R;                r = P;            }        }

Transient node<k,v>[] table;//storage (bit bucket) array

With the above 3 data structures, as long as there is a bit of data structure based on people, can be roughly associated with the implementation of HashMap. First, each element is an array of linked lists (which may be inaccurate), and when an element (Key-value) is added, the hash value of the element key is computed first to determine the position in the insertion array, but the element with the same hash value may have been placed in the same position as the array. This is then added to the same hash value after the element, they are in the same position in the array, but formed a linked list, so that the array is stored in the linked list. When the list length is too long, the list is converted to a red-black tree, which greatly improves the efficiency of the search.

The following continues the code implementation:

2 HashMap Main Properties

Say the padding ratio, the default value is 0.75, if the actual element occupies 75% of the allocated capacity will be expanded. If the fill ratio is very large, the use of space is much, but the efficiency of the search is very low, because the length of the list is very large (of course, the latest version of the use of red and black trees will improve a lot), HashMap originally to space for time, so fill than not necessarily too big. However, the filling ratio is too small and can lead to wasted space. If you focus on memory, the fill ratio can

Slightly larger, if the main focus is on finding performance, the fill ratio can be slightly smaller.

public class Hashmap<k,v> extends abstractmap<k,v> implements Map<k,v>, Cloneable, Serializable {    private static final long serialversionuid = 362498820763181265L;    static final int default_initial_capacity = 1 << 4; AKA-    static final int maximum_capacity = 1 << 30;//maximum capacity    static final float default_load_factor = 0.75f; Fill ratio    //When add an element to a bit bucket, its list length reaches 8 when the linked list is converted to a red-black tree    static final int treeify_threshold = 8;    static final int untreeify_threshold = 6;    static final int min_treeify_capacity =;    Transient node<k,v>[] table;//An array of storage elements    transient set<map.entry<k,v>> entryset;    transient int size;//The number of elements stored    transient int modcount;//The number of times modified fast-fail mechanism    int threshold;//critical worth actual size (capacity * fill ratio) When the threshold is exceeded, the    final float loadfactor;//fill ratio (...) is expanded. Back slightly)

3 Construction Methods

There are 4 ways to construct the HashMap, which mainly involves parameters, specifying initial capacity, specifying the fill ratio and the map used to initialize, and looking directly at the code

    /*----------------Public Operations--------------*//constructor 1 public HashMap (int initialcapacity, float LOADFA ctor) {//specified initial capacity non-negative if (Initialcapacity < 0) throw new illegalargumentexception ("illegal initial C        Apacity: "+ initialcapacity);    If the initial capacity specified is greater than the maximum capacity, set to maximum capacity if (Initialcapacity > maximum_capacity) initialcapacity = maximum_capacity; Fill ratio is positive if (loadfactor <= 0 | |                                               Float.isnan (Loadfactor)) throw new IllegalArgumentException ("Illegal load factor:" +        Loadfactor);        This.loadfactor = Loadfactor; This.threshold = Tablesizefor (initialcapacity);//New expansion Critical Value}//Constructor 2 public HashMap (int initialcapacity) {T    His (initialcapacity, default_load_factor); }//Constructor 3 public HashMap () {this.loadfactor = Default_load_factor;//All and fields defaulted}//Structure    Constructor 4 Initializes a hash map with elements of MPublic HashMap (map<? extends K,? extends v> m) {this.loadfactor = Default_load_factor;    Putmapentries (M, false); }

4 expansion mechanism

When constructing a hash table, if the initial size is not indicated, the default size is 16 (that is, the Node array size 16), if the elements in the node[] array are reached (fill ratio *node.length)

 Can be used to initialize the HashMap size or resize the hashmap size to twice times the original size 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) {//exceeds 1>>30 size, no expansion can only change thresholds threshold                = Integer.max_value;            return oldtab; } else if ((Newcap = oldcap << 1) < maximum_capacity && Oldcap >= Defau lt_initial_capacity)//new capacity for the old twice times the smallest is also newthr = Oldthr << 1; Expansion threshold doubled} else if (Oldthr > 0) newcap = oldthr;//oldcap=0, oldthr>0 at this time newthr=0 el            SE {//oldcap=0,oldthr=0 is equivalent to initializing Newcap = Default_initial_capacity with the default fill ratio and initial capacity;        NEWTHR = (int) (Default_load_factor * default_initial_capacity); } if (Newthr = = 0) {floatft = (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];        Array auxiliary to the new array, dividend black tree and linked list discussion 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;                            do {next = E.next; if ((E.hash & oldcap) = = 0) {if (Lotail = = null) lo                                Head = 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;                        Newtab[j + oldcap] = Hihead;    }}}}} return newTab; }

Obviously, the expansion is time-consuming because of the operations of the old array elements being copied into the new array.

5 determining the array of element Put/get node[] Position

    Static final int hash (Object key) {        int h;        return (key = = null)? 0: (H = key.hashcode ()) ^ (h >>>);    }
public native int hashcode ();

First, the hash value H is obtained by the key value via hash (key), and then the location of the array is obtained by h& (length-1). Generally for hash tables commonly used methods have a direct addressing method, in addition to the remainder method, both to facilitate the calculation, but also to reduce the conflict.

In Hashtable, the distribution is by the remainder method, as follows:

int index = (hash & 0x7FFFFFFF)% Tab.length;
However, the efficiency of division operation is very low, and the HashMap is replaced by h& (LENGTH-1) to get the array position, so the efficiency is much higher.

In the HashMap implementation, you can also see that the following code replaces the previous version of JDK1.6 's while loop to ensure that the hash table's capacity is always an integer multiple of 2, replacing the cyclic shift with a shift operation.

    This code guarantees that the HASHMAP capacity is always 2 of the n-th    static final int tablesizefor (int cap) {        int n = cap-1;        n |= n >>> 1;        N |= n >>> 2;        N |= n >>> 4;        N |= n >>> 8;        n |= n >>>;        Return (n < 0)? 1: (n >= maximum_capacity)? Maximum_capacity:n + 1;    }
Can be seen from the source code, in the HashMap constructor, are directly or indirectly called the Tablesizefor function. The following analysis causes: The length of the integer power of 2 guarantees that the Length-1 last (of course, the binary representation) is 1, thus guaranteeing the index operation h& (LENGTH-1) The last one at the same time there is a possibility of 0 and 1, ensuring the uniformity of the hash. Conversely, when length is odd, the last digit of length-1 is 0, so with the H bitwise AND

The last one must be 0, that is, the index position is definitely an even number, so that the odd position of the array does not place all elements, wasting a lot of space.

In short: a power of length of 2 guarantees the validity of the bitwise and last one, making the hash table hash more homogeneous.

6 The following analysis HashMap the most common operations put and get

Note that both key and value in HashMap are allowed to be null

Directly on the code:

    get***************************************************/public V get (Object k        EY) {node<k,v> e; Return (E = getnode (hash (key), key) = = null?    Null:e.value; } final node<k,v> getnode (int hash, Object key) {node<k,v>[] tab; Node<k,v> First, E; int n;        K K;            Hash & (Length-1) Gets the object's save bit if (tab = table)! = null && (n = tab.length) > 0 &&                (first = tab[(n-1) & hash]) = null) {if (First.hash = = Hash &&//Always check first node (k = first.key) = = Key | |                (Key! = null && key.equals (k))))            return first; if ((e = first.next) = null) {//If the first node is TreeNode, the description uses an array + red-black tree structure to handle the conflict//traverse the red-black tree, get the node value I                F (First instanceof TreeNode) return ((treenode<k,v>) first). Gettreenode (hash, key);    Linked list structure processing do {                if (E.hash = = Hash && (k = e.key) = = Key | |                        (Key! = null && key.equals (k))))                return e;            } while ((e = e.next) = null);    }} return null; }

 put********************************************************************* 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& Gt [] tab; Node<k,v> p;        int n, I; If tab is empty or length is 0, allocate memory resize () if (tab = table) = = NULL | |        (n = tab.length) = = 0) n = (tab = resize ()). length; (n-1) & hash Find put position, if empty, then put if (p = tab[i = (n-1) & hash]) = = = null) tab[i] = NewNode (        Hash, key, value, NULL); else {node<k,v> E;            K K; The first point hash value is the same, and the key value is the same as the Insert key if (P.hash = = Hash && (k = p.key) = = Key | |                (Key! = null && key.equals (k))))            e = p; else if (P instanceof TreeNode)//belongs to the red black tree processing conflict e = ((treenode<k,v>) p). puttreeval (This, tab, hash, key, VA  Lue);          else {//List handling conflict for (int bincount = 0;; ++bincount) {//p First point to table header to if ((e = p.next) = = null) {//e is empty, indicating that the key value is not found at the end of the table and the same node, the new node P.N                ext = NewNode (hash, key, value, NULL);                            If the number of nodes reaches the threshold after the new node, convert the linked list to a red-black tree if (bincount >= treeify_threshold-1)//-1 for 1st                        Treeifybin (tab, hash);                    Break }//Allow null==null if (E.hash = = Hash && (k = e.key) = = Key | |                        (Key! = null && key.equals (k))))                    Break p = e;//Update p refers to the next node}}//update hash value and key value are the same node value if (E! = null) {//Exis                Ting 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; }

The following is simply the process of adding a key-value pair put (Key,value): (In fact, look directly at the code logic more clearly)

1 Determine whether the key value of the array tab[] is empty or null, otherwise resize ();

2 Calculate hash value based on key value key to get inserted array index I, if tab[i]==null, add directly new node, otherwise transfer to 3

3 Determines whether the current array handles hash collisions in the form of a linked list or a red-black tree (check the first node type), respectively.


Reprint Please declare: http://blog.csdn.net/u010498696/article/details/45888613

Java class set Framework HashMap (JDK1.8) Source code Analysis

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.