First, preface
The previous analysis of HashMap and Linkedhashmap, now we analyze the less common Identityhashmap, from its name can also be seen to represent the only HashMap, carefully analyzed its source code, It is found that the data structure is completely different from the data structure used by HASHMAP because they have no relationship with each other on the inheritance relationship. Below, enter our analysis phase.
Ii. Examples of Identityhashmap
Import Java.util.map;import Java.util.hashmap;import Java.util.identityhashmap;public class IdentityHashMapTest { Public static void Main (string[] args) { map<string, string> hashmaps = new hashmap<string, string> ( ); map<string, string> identitymaps = new identityhashmap<string, string> (); Hashmaps.put (New String ("AA"), "AA"); Hashmaps.put (New String ("AA"), "BB"); Identitymaps.put (New String ("AA"), "AA"); Identitymaps.put (New String ("AA"), "BB"); System.out.println (hashmaps.size () + ":" + hashmaps); System.out.println (identitymaps.size () + ":" + Identitymaps);} }
Operation Result:
1: {AA=BB}
2: {AA=BB, AA=AA}
Description: Identityhashmap is overwritten only if the key is exactly equal (the same reference), and HashMap does not.
Third, IDENTITYHASHMAP data structure
Description: Identityhashmap data is very simple, the underlying is actually an object array, logically need to be seen as a ring array, the solution is to resolve the conflict is: based on the calculation of the hash position, if there are already elements found in the location, then look back until the empty position, Storage, if not, directly to the storage. When the number of elements reaches a certain threshold, the object array is automatically scaled to handle the expansion.
Four, Identityhashmap source analysis
Inheritance relationships for Class 4.1
public class identityhashmap<k,v> extends abstractmap<k,v> implements MAP<K,V> Java.io.Serializable, cloneable
Description: Inherits the Abstractmap abstract class, implements the map interface, can serialize the interface, can clone the interface.
Properties of Class 4.2
public class identityhashmap<k,v> extends abstractmap<k,v> implements MAP<K,V> Java.io.Serializable, cloneable{ //default capacity size private static final int default_capacity = +; Minimum capacity private static final int minimum_capacity = 4; Maximum capacity private static final int maximum_capacity = 1 <<; Table for storing the actual elements transient object[] table; Sizes int size; The number of structural modifications to the map transient int modcount; The value corresponding to null KEY is static final object Null_key = new Object ();}
Note: You can see that the bottom layer of the class is using an object array to hold the elements.
constructors for Class 4.3
1. Identityhashmap () type constructor
Public Identityhashmap () { init (default_capacity);}
Public Identityhashmap () { init (default_capacity);}
2. Identityhashmap (int) type constructor
Public identityhashmap (int expectedmaxsize) { if (expectedmaxsize < 0) throw new IllegalArgumentException (" Expectedmaxsize is negative: " + expectedmaxsize); Init (Capacity (expectedmaxsize)); }
Public identityhashmap (int expectedmaxsize) { if (expectedmaxsize < 0) throw new IllegalArgumentException (" Expectedmaxsize is negative: " + expectedmaxsize); Init (Capacity (expectedmaxsize)); }
3. Identityhashmap (map<? extends K,? extends v>)-type constructors
Public Identityhashmap (map<? extends K,? extends v> m) { //Call other constructors this ((int) ((1 + m.size ()) * 1.1)); C14/>putall (m); }
Public Identityhashmap (map<? extends K,? extends v> m) { //Call other constructors this ((int) ((1 + m.size ()) * 1.1));
putall (m); }
4.4 Important Function Analysis
1. Capacity function
The value returned by this function is a minimum greater than expectedmaxsize of 2 power private static int capacity (int expectedmaxsize) { //Assert Expectedmaxsize >= 0; Return (Expectedmaxsize > Maximum_capacity/3)? Maximum_capacity: (expectedmaxsize <= 2 * minimum_capacity/3)? Minimum_capacity: integer.highestonebit (expectedmaxsize + (expectedmaxsize << 1)); }
The value returned by this function is a minimum greater than expectedmaxsize of 2 power private static int capacity (int expectedmaxsize) { //Assert Expectedmaxsize >= 0; Return (Expectedmaxsize > Maximum_capacity/3)? Maximum_capacity: (expectedmaxsize <= 2 * minimum_capacity/3)? Minimum_capacity: integer.highestonebit (expectedmaxsize + (expectedmaxsize << 1)); }
Description: The value returned by this function is the smallest and greater than the value of 2 powers of expectedmaxsize.
2. Hash function
hash function, because length is always 2 of the power of N, so & (length-1) equivalent to length modulo private static int hash (Object x, int length) { int h = System.identityhashcode (x); Multiply by-127, and left-shift to use least bits as part of the hash return ((H << 1)-(H << 8)) & ( length-1); }
hash function, because length is always 2 of the power of N, so & (length-1) equivalent to length modulo private static int hash (Object x, int length) { int h = System.identityhashcode (x); Multiply by-127, and left-shift to use least bits as part of the hash return ((H << 1)-(H << 8)) & (L ength-1); }
Description: The hash function is used for hashing and guarantees that the hash value of the element is even indexed in the array.
3. Get function
Public V get (object key) { //Guaranteed NULL key will be converted to object (Null_key) Object k = Masknull (key); Save table object[] tab = table; int len = tab.length; Get the hash position of the key int i = hash (k, Len); To traverse the table, resolve the hash conflict by searching for the idle area while (true) { Object item = tab[i] If the conflict occurs; Determine if equality (address is equal) if (item = = k) //address equal, i.e. exactly equal two objects return (V) Tab[i + 1]; If the element corresponding to the hash position is empty, then an empty if (item = = NULL) return null; Remove a key index i = Nextkeyindex (i, Len); } }
Public V get (object key) { //Guaranteed NULL key will be converted to object (Null_key) Object k = Masknull (key); Save table object[] tab = table; int len = tab.length; Get the hash position of the key int i = hash (k, Len); To traverse the table, resolve the hash conflict by searching for the idle area while (true) { Object item = tab[i] If the conflict occurs; Determine if equality (address is equal) if (item = = k) //address equal, i.e. exactly equal two objects return (V) Tab[i + 1]; If the element corresponding to the hash position is empty, then an empty if (item = = NULL) return null; Remove a key index i = Nextkeyindex (i, Len); } }
Description: The function compares whether the key value is identical (the object type is the same reference, and the base type is equal to the content)
4. Nextkeyindex function
Next key index private static int nextkeyindex (int i, int len) { //Move backward two units return (i + 2 < len? i + 2:0 ); }
Next key index private static int nextkeyindex (int i, int len) { //Move backward two units return (i + 2 < len i + 2:0); c3/>}
Description: This function is used to determine when a conflict occurs and a location is removed.
5. Put function
Public V put (K key, V value) {//Guaranteed NULL key will be converted to object (Null_key) Final Object K = Masknull (key); Retryafterresize:for (;;) {Final object[] tab = table; final int len = tab.length; int i = hash (k, Len); for (Object item; (item = Tab[i])! = NULL; i = Nextkeyindex (i, Len)) {if (item = = k) {//hash-calculated items are equal to key @SuppressWarnings ("Unch Ecked ")//Acquisition value V OldValue = (v) tab[i + 1]; Deposit value Tab[i + 1] = value; Returns the old value return oldValue; }}//size plus 1 final int s = size + 1; Use optimized form of 3 * s. Next capacity is Len, 2 * current capacity. If 3 * size is greater than length, the expansion operation will be performed if (S + (s << 1) > Len && resize (len))//After expansionRecalculate the value of the element, find the appropriate location to store continue retryafterresize; Structural modification plus 1 modcount++; Store key with value tab[i] = k; Tab[i + 1] = value; Update size size = s; return null; } }
Public V put (K key, V value) {//Guaranteed NULL key will be converted to object (Null_key) Final Object K = Masknull (key); Retryafterresize:for (;;) {Final object[] tab = table; final int len = tab.length; int i = hash (k, Len); for (Object item; (item = Tab[i])! = NULL; i = Nextkeyindex (i, Len)) {if (item = = k) {//hash-calculated items are equal to key @SuppressWarnings ("Unch Ecked ")//Acquisition value V OldValue = (v) tab[i + 1]; Deposit value Tab[i + 1] = value; Returns the old value return oldValue; }}//size plus 1 final int s = size + 1; Use optimized form of 3 * s. Next capacity is Len, 2 * current capacity. If 3 * size is greater than length, the expansion operation will be performed if (S + (s << 1) > Len && resize (len))//After expansionRecalculate the value of the element, find the appropriate location to store continue retryafterresize; Structural modification plus 1 modcount++; Store key with value tab[i] = k; Tab[i + 1] = value; Update size size = s; return null; } }
Note: If the incoming key already exists in the table (emphasizing that it is the same reference), the new value is substituted for the old value and the old value is returned, and if the number of elements reaches the threshold, then the capacity is expanded, and then the key and value are found in the appropriate location.
6. Resize function
Private Boolean resize (int newcapacity) {//Assert (Newcapacity &-newcapacity) = = newcapacity;//Power of 2 int newlength = newcapacity * 2; Save the original table object[] oldtable = table; int oldlength = Oldtable.length; Whether the old table is twice times the maximum capacity if (Oldlength = = 2 * maximum_capacity) {//can ' t expand any further//before the number of elements is maximum capacity, throws an exception if (size = = maximum_capacity-1) throw new IllegalStateException ("Capacity exhausted."); return false; }//Old table length is greater than new table length, return False if (Oldlength >= newlength) return false; Generate new Table object[] newtable = new Object[newlength]; Re-hash all elements in the old table into the new table for (int j = 0; J < oldlength; J + = 2) {Object key = Oldtable[j]; if (key = null) {Object value = oldtable[j+1]; OLDTABLE[J] = null; OLDTABLE[J+1] = null; int i = hash (key, Newlength); while (newtable[i]! = null) i = Nextkeyindex (i, newlength); Newtable[i] = key; Newtable[i + 1] = value; }}//New table is assigned value to table table = newtable; return true; }
Private Boolean resize (int newcapacity) {//Assert (Newcapacity &-newcapacity) = = newcapacity;//Power of 2 int newlength = newcapacity * 2; Save the original table object[] oldtable = table; int oldlength = Oldtable.length; Whether the old table is twice times the maximum capacity if (Oldlength = = 2 * maximum_capacity) {//can ' t expand any further//before the number of elements is maximum capacity, throws an exception if (size = = maximum_capacity-1) throw new IllegalStateException ("Capacity exhausted."); return false; }//Old table length is greater than new table length, return False if (Oldlength >= newlength) return false; Generate new Table object[] newtable = new Object[newlength]; Re-hash all elements in the old table into the new table for (int j = 0; J < oldlength; J + = 2) {Object key = Oldtable[j]; if (key = null) {Object value = oldtable[j+1]; OLDTABLE[J] = null; OLDTABLE[J+1] = null; int i = hash (key, Newlength); while (newtable[i]! = null) i = Nextkeyindex (i, newlength); Newtable[i] = key; Newtable[i + 1] = value; }}//New table is assigned value to table table = newtable; return true; }
Note: When the element in the table reaches the threshold, it expands processing, and the elements in the old table are re-hashed into the new table after the expansion.
7. Remove function
Public V Remove (Object key) { //Guaranteed NULL key will be converted to object (Null_key) Object k = Masknull (key); object[] tab = table; int len = tab.length; Computes the hash value int i = hash (k, Len); while (true) { Object item = tab[i]; Find key equal items if (item = = k) { modcount++; size--; @SuppressWarnings ("unchecked") V oldValue = (V) Tab[i + 1]; Tab[i + 1] = null; Tab[i] = null; After the deletion, the subsequent processing is required to move the elements that were previously moved by the conflict to the front to closedeletion (i); return oldValue; } The item is an empty if (item = = NULL) return null; Next item i = Nextkeyindex (i, Len);} }
Public V Remove (Object key) { //Guaranteed NULL key will be converted to object (Null_key) Object k = Masknull (key); object[] tab = table; int len = tab.length; Computes the hash value int i = hash (k, Len); while (true) { Object item = tab[i]; Find key equal items if (item = = k) { modcount++; size--; @SuppressWarnings ("unchecked") V oldValue = (V) Tab[i + 1]; Tab[i + 1] = null; Tab[i] = null; After the deletion, the subsequent processing is required to move the elements that were previously moved by the conflict to the front to closedeletion (i); return oldValue; } The item is an empty if (item = = NULL) return null; Next item i = Nextkeyindex (i, Len);} }
8. Closedeletion function
private void closedeletion (int d) {//Adapted from Knuth section 6.4 algorithm R object[] tab = table; int len = tab.length; Look for items to swaps into newly vacated slots//starting at index immediately following deletion,//an D continuing until a null slot is seen, indicating//the end of a run of possibly-colliding keys. Object item; Move the element that follows the move rule forward to the front for (int i = Nextkeyindex (d, Len); (item = Tab[i])! = NULL; i = Nextkeyindex (i, Len)) {//The following test triggers if the item at slot I (which//hashes T o be in slot R) should take the spot vacated by D. If So, we swaps it in, and then continue with D now at the//newly vacated I. This process would terminate when we hit//the null slot at the end of this run. The test is messy because, we are using a circular table. int r = Hash (item, Len); IF ((I < R && (r <= D | | d <= i)) | | (R <= D && d <= i)) {Tab[d] = Item; Tab[d + 1] = tab[i + 1]; Tab[i] = null; Tab[i + 1] = null; d = i; } } }
private void closedeletion (int d) {//Adapted from Knuth section 6.4 algorithm R object[] tab = table; int len = tab.length; Look for items to swaps into newly vacated slots//starting at index immediately following deletion,//an D continuing until a null slot is seen, indicating//the end of a run of possibly-colliding keys. Object item; Move the element that follows the move rule forward to the front for (int i = Nextkeyindex (d, Len); (item = Tab[i])! = NULL; i = Nextkeyindex (i, Len)) {//The following test triggers if the item at slot I (which//hashes T o be in slot R) should take the spot vacated by D. If So, we swaps it in, and then continue with D now at the//newly vacated I. This process would terminate when we hit//the null slot at the end of this run. The test is messy because, we are using a circular table. int r = Hash (item, Len); IF ((I < R && (r <= D | | d <= i)) | | (R <= D && d <= i)) {Tab[d] = Item; Tab[d + 1] = tab[i + 1]; Tab[i] = null; Tab[i + 1] = null; d = i; } } }
Description: When an element is deleted, it is closedeletion processed once and the position of the element is reassigned.
Closedeletion before and after the closedeletion.
Description: The Assumption: Wherein, ("AA" and "AA") after the hash in the No. 0, ("BB", "BB") after the hash should also be in 0, conflict, move back to the 2nd, ("CC" and "CC") after the hash in the 2nd, there is a conflict, Move back to the 4th item, ("GG", "GG") after the hash in the 2nd item, the conflict, move backward to the 6th item ("DD", "DD") in the 8th item ("EE", "ee") in the 12th item. When deleted ("BB", "BB"), the layout of the elements after processing is shown in the image on the right.
V. Summary
Identityhashmap and HashMap are very different in data structures, and the methods of dealing with hash collisions are not the same. Where Identityhashmap is considered identical only when key is the same reference, and HashMap includes equals equal, that is, the content is the same.
Java Collection Class Analysis-identyhashmap