HashMap Source Analysis (JDK1.7)

Source: Internet
Author: User
Tags rehash

See HashMap Source has a period of time, but has not written something, these days, while to change internship company, nothing to do, put their understanding of hashmap write down, while writing to organize their own ideas.

This is a drawing of someone else's drawing to understand the HashMap, the simple understanding is that it combines the advantages of the array lookup fast and the list insertion delete fast.

The following direct analysis of source code:

Start with the constructor:

Public HashMap (int initialcapacity, float loadfactor) {if (Initialcapacity < 0) throw new IllegalArgumentException (" Illegal initial capacity: "+ initialcapacity); if (Initialcapacity > maximum_capacity) initialcapacity = MAXIMUM_ Capacity;if (loadfactor <= 0 | | Float.isnan (Loadfactor)) throw new IllegalArgumentException ("Illegal load factor:" + loadfactor);// Find a minimum number greater than initialcapacity and a power of 2 int capacity = 1;while (capacity < initialcapacity) capacity <<= 1; This.loadfactor = Loadfactor;    Since loadfactor can be a number greater than 1, this prevents threshold from exceeding the maximum capacity threshold = (int) math.min (capacity * Loadfactor, maximum_capacity + 1); Table = new Entry[capacity];init ();}

Although there are also several overloaded constructors, this constructor is called through the This keyword.

So in fact HashMap used to store the data structure is a entry[], white is the number of groups, then we look at entry this class, which is a static internal class, comments are written, some did not say the source is not posted up, you can go to see, such as equals (). The following is the source of this class to paste out:

Static Class Entry<k,v> implements Map.entry<k,v> {final K key;//saved key,final keyword defined once the assignment cannot be modified, so there is no set The key method V value;//holds the value entry<k,v> next;//points to the next Entry reference int hash;//The saved hash value Entry (int        H, K K, v V, entry<k,v> N) {value = V;next = N;key = K;hash = h;        } Public final K GetKey () {return key;}        Public final V GetValue () {return value;}            Public final V SetValue (v newvalue) {v oldValue = value;            value = newvalue;        return oldValue; } public final int hashcode () {return (key==null? 0:key.hashcode ()) ^ (Value==nu ll?        0:value.hashcode ());        }//Override ToString, easy to print display public final String toString () {return GetKey () + "=" + GetValue ();        }//In the Put () method, when a entry key is stored with the key in the same time, the existing entry will be overwritten, and call this method, I think it should be for the inheriting HashMap class itself. void Recordaccess (hashmap<k,v> m) {}                Similar to Recordaccess, this method is called when entry is removed. void Recordremoval (hashmap<k,v> m) {}}

Before the most commonly used put () method before the science:Hash, the general translation to do "hash", there is a direct transliteration of the "hash", is the arbitrary length of the input (also known as pre-mapping, pre-image), through the hash algorithm, transformed into a fixed-length output, the output is the hash value. This conversion is a compression map, that is, the space of the hash value is usually much smaller than the input space , the different inputs may be hashed to the same output, but not from the hash value to uniquely determine the input value .

Put ()

Associate a specific key with a specific value, if there is a mapping for a key first in the map, OldValue will be replaced with public V put (K key, V value) {        //If key is null, The Putfornullkey method is called        if (key = = null)            return Putfornullkey (value);        Calculates the hash value        int hash = hash (key) according to key;        Calculates the array subscript        int i = indexfor (hash, table.length) according to hash value and array size;        Note 1 for        (entry<k,v> e = table[i]; E! = null; e = e.next) {            Object K;            if (E.hash = = Hash && (k = e.key) = = Key | | key.equals (k))) {                V oldValue = e.value;                E.value = value;                E.recordaccess (this);                return oldValue;            }        }        Number of revisions recorded +1        modcount++;        Put the entry object into the array        addentry (hash, key, value, I);        return null;}

Note 1:

Based on the subscript index to E, if E is not empty, the key in key and E is determined by the hash value. How do you judge it?

1. First determine the hash value, if the hash value is not the same, it is certainly not the same object (and vice versa), there is no need to waste time.

2. If the hash value is the same, then further determine whether the key in E is the same, in two ways: "= =" reference judgment and. Equals () value to judge (can self-override), if it is the same coincidence, the old value is returned and overwritten, and call the Recordaccess () method described earlier.

After locating the position I, the addentry () implementation joins a entry:

void AddEntry (int hash, K key, V value, int bucketindex) {        //If the entry number is greater than the threshold and the current position I already has an element, call resize () Method and passes in a parameter twice the size of the current array.        if (size >= threshold) && (null! = Table[bucketindex])) {            Resize (2 * table.length);            hash = (Null! = key)? Hash (key): 0;            Bucketindex = Indexfor (hash, table.length);        }        Create a new entry        createentry (hash, key, value, Bucketindex),} void Createentry (int hash, K key, V value, int bucketindex) {        entry<k,v> e = Table[bucketindex];        Table[bucketindex] = new entry<> (hash, key, value, e);        size++;}

This method is ingenious, create a entry reference E to the original entry, and then point to the array I position of the reference to E (in the construction method of entry), so that the newly created entry is at the forefront of the list, a bit like the FIFO of the stack. Finally, the size++.

Take a look at the Resize () method:

void Resize (int newcapacity) {        entry[] oldtable = table;        Get the array size, if it is greater than the maximum capacity, then set the threshold to Integer.max_value, then no longer trigger the resize () operation, solve everything        int oldcapacity = oldtable.length;        if (oldcapacity = = maximum_capacity) {            threshold = Integer.max_value;            return;        }         entry[] newtable = new entry[newcapacity];        Below can not understand, 1.6 does not rehash this parameter        boolean oldalthashing = usealthashing;        Usealthashing |= sun.misc.VM.isBooted () &&                (newcapacity >= holder.alternative_hashing_threshold);        Boolean rehash = oldalthashing ^ usealthashing;         Focus here, as can be seen in transfer (), to transfer all the entry in the table to newtable        transfer (newtable, rehash);        Change the array that the table points to and reset the threshold value        table = newtable;        threshold = (int) math.min (newcapacity * loadfactor, maximum_capacity + 1);}

Key to see Transfer ():

void Transfer (entry[] newtable, Boolean rehash) {        int newcapacity = newtable.length;        According to entry E, which starts with each linked list in the array, gets its next next, and if key is not null (some key is indeed null), the hash value is updated. Calculates the subscript in the new array based on the new hash value, and then points to that position. The last for        (entry<k,v> e:table) {            while (null! = e) {                entry<k,v> next = e.next;                if (rehash) {                    E.hash = NULL = = E.key? 0:hash (E.key);                }                int i = indexfor (E.hash, newcapacity);                E.next = Newtable[i];                Newtable[i] = e;                e = Next;}}}        

The last three lines of code compare need to take the effort to understand,

The first line: E.next points to newtable[i], no longer points to the next element in the owning list, but the next reference saves the modified element

The second line: Newtable[i] pointing to e,newtable[i] is null and now points to the first element in the list of E-owned

The third line: E points to next, meaning that e no longer points to the first element in the list, but to its next element


If you do not understand that also normal, see more than two times must be able to understand, I have seen several times.

For example, like shooting, the first element e of a list in a while,table is like a bullet, fired at NewTable, E points to the element being shot (E.next = Newtable[i]), where a pit is shot, The original position is replaced by E (newtable[i] = e), and then the E vacancy is filled with next (E = next), as is the case with automatic loading of ammunition. (Think for a long time to understand, this is the result of data structure bad)

There's one left.

Private V Putfornullkey (v value) {    //all key null Entry are saved in the array labeled 0 on the linked list for        (entry<k,v> e = table[0]; E! = null; E = e.next) {            //Find entry E NOT null (this is necessary)            if (E.key = = null) {                V oldValue = e.value;                E.value = value;                E.recordaccess (this);                return oldValue;            }        }        modcount++;        Also call AddEntry (), add a Entry,hash value of 0,key to null, subscript 0        addentry (0, NULL, value, 0);        return null;}

The put () method is almost the same, in fact, the corresponding other common method get () is similar, everyone is interested in reading.

Let's take a look at the delete operation:

Remove ()

Public V Remove (Object key) {        entry<k,v> e = Removeentryforkey (key);        return (E = = null? null:e.value);}

Removeentryforkey () returns an e that is to be removed, if e==null, returns NULL, otherwise returns the value in E

Final entry<k,v> Removeentryforkey (Object key) {    //both lines have seen        int hash = (key = = null) in put ()? 0:hash (key); 
   int i = indexfor (hash, table.length);        Entry<k,v> prev = table[i];        entry<k,v> e = prev;         while (E! = null) {            entry<k,v> next = e.next;            Object K;            if (E.hash = = Hash &&                (k = e.key) = = Key | | (Key! = null && key.equals (k)))) {                modcount++;                size--;                If you delete the head node (table[i]) if                (prev = = e)                    table[i] = next;                else                    prev.next = next;                E.recordremoval (this);                return e;            }            The object reference moves backwards, like the pointer in C moves backwards            prev = e;            e = next;        }        return e;}

The above operation is the same as deleting the linked list node (I do not have a good data structure, it looks more difficult), the difference is that if you delete the head node (Table[i]) there is a difference.

It's also deleted.

public void Clear () {        modcount++;        entry[] tab = table;        for (int i = 0; i < tab.length; i++)            tab[i] = null;        size = 0;}

It is visible that each reference to the header of the Table[i] is set to null, so that the garbage collection mechanism collects and frees up space at the appropriate time.

HashMap Source Analysis (JDK1.7)

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.