HashMap archiving-beyond yesterday's series, hashmap Archiving
Java HashMap
Read the source code to store an array of data:
transient Entry[] table;
Internal classes with internal key and value:
static class Entry<K,V> implements Map.Entry<K,V> { final K key; V value; Entry<K,V> next; final int hash; /** * Creates new entry. */ 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 boolean equals(Object o) { if (!(o instanceof Map.Entry)) return false; Map.Entry e = (Map.Entry)o; Object k1 = getKey(); Object k2 = e.getKey(); if (k1 == k2 || (k1 != null && k1.equals(k2))) { Object v1 = getValue(); Object v2 = e.getValue(); if (v1 == v2 || (v1 != null && v1.equals(v2))) return true; } return false; } public final int hashCode() { return ( key== null ? 0 : key.hashCode()) ^ ( value== null ? 0 : value.hashCode()); } public final String toString() { return getKey() + "=" + getValue(); } /** * This method is invoked whenever the value in an entry is * overwritten by an invocation of put(k,v) for a key k that's already * in the HashMap. */ void recordAccess(HashMap<K,V> m) { } /** * This method is invoked whenever the entry is * removed from the table. */ void recordRemoval(HashMap<K,V> m) { } }
Therefore, the data structure of HashMap is the linked list structure under the array,
Public V put (K key, V value) {if (key = null) return putForNullKey (value); int hash = hash (key. hashCode (); int I = indexFor (hash, table. length); // locate the position in the Array Using hash // obtain the 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 ;}} modCount ++; // Add a new value to the position of the array without the same key into addEntry (hash, key, value, i); return null ;}
From the perspective of performance, the longer the chain table length is, the longer it takes to find whether a key is in this map. The key on the same linked list is determined by key. hashCode.
It is also noted that when null is the key object, it is directly placed in the header of the array. This is also the best implementation. Otherwise, a null value must be searched again.
private V putForNullKey(V value) { for (Entry<K,V> e = table[0]; e != null; e = e. next) { if (e. key == null) { V oldValue = e. value; e. value = value; e.recordAccess( this); return oldValue; } } modCount++; addEntry(0, null, value, 0); return null; }
When adding new data to the linked list, you need to check whether the resize is required. The condition is that the length of the occupied array has reached the current length of the array * loadFactor (0.75 ), that is to say, when the array is used to 75 percent, it needs to be expanded. Resize means that the array is doubled, so you need to re-hash all existing entries in the expanded array to the new location. Imagine many elements, performance will inevitably be affected.
void addEntry (int hash, K key, V value, int bucketIndex) { Entry<K,V> e = table[bucketIndex]; table[bucketIndex] = new Entry<K,V>(hash, key, value, e); if (size++ >= threshold) resize(2 * table. length); }
HashMap hash algorithm:
static int hash(int h) { // This function ensures that hashCodes that differ only by // constant multiples at each bit position have a bounded // number of collisions (approximately 8 at default load factor). h ^= (h >>> 20) ^ (h >>> 12); return h ^ (h >>> 7) ^ (h >>> 4); } /** * Returns index for hash code h. */ static int indexFor(int h, int length) { return h & (length-1); }
This should take a look at in detail, hash algorithm in the previous article has a record, you can see: http://www.cnblogs.com/killbug/p/4560000.html
In order to try to evenly distribute the put data on the array and improve the map performance, the above two methods are used to do this.
The hash method returns the key. the hashcode is re-hashed. the hash function changes the "1 bit" of hashcode to "loose" by several shifts, excludes, or operations ", in the next step with the length of the array, we can get a more average array subscript.
We noticed that the length of the array is required to be the power of 2, so it is best to reduce the value of 1 to 1111 during the operation.
The two groups on the left are 16 in length (4 to the power of 2), and the two groups on the right are 15 in length. The hashcodes of the two groups are 8 and 9, but it is obvious that when they and 1110 "and", the same results are generated, that is to say, they will be located in the same position in the array, which produces a collision. 8 and 9 will be placed on the same linked list, so you need to traverse this linked list during query, get 8 or 9, which reduces the query efficiency. At the same time, we can also find that when the array length is 15, the hashcode value will be "and" with 14 (1110), then the last bit will always be 0, and 0001,0011, 0101,1001, 1011,0111, 1101 these locations can never store elements, and the space waste is quite large. Worse, in this case, the positions available for arrays are much smaller than the array length, this means that the probability of collision is further increased and the query efficiency is slowed down!
Public HashSet () {map = new HashMap <E, Object> ();}
For example, the contains function:
public boolean contains(Object o) { return map.containsKey(o); }
LinkedHashMapIt is also frequently used. It integrates HashMap. The structure is the same as that of HashMap. In order to achieve sequential access, the order is recorded when an element (Entry) is put.
The Entry class also inherits HashMap, and then adds before and after to record the order:
/** * LinkedHashMap entry. */ private static class Entry<K,V> extends HashMap.Entry<K,V> { // These fields comprise the doubly linked list used for iteration. Entry<K,V> before, after; Entry(int hash, K key, V value, HashMap.Entry<K,V> next) { super(hash, key, value, next); } /** * Removes this entry from the linked list. */ private void remove() { before.after = after; after.before = before; } /** * Inserts this entry before the specified existing entry in the list. */ private void addBefore(Entry<K,V> existingEntry) { after = existingEntry; before = existingEntry.before; before.after = this; after.before = this; } /** * This method is invoked by the superclass whenever the value * of a pre-existing entry is read by Map.get or modified by Map.set. * If the enclosing Map is access-ordered, it moves the entry * to the end of the list; otherwise, it does nothing. */ void recordAccess(HashMap<K,V> m) { LinkedHashMap<K,V> lm = (LinkedHashMap<K,V>)m; if (lm.accessOrder) { lm.modCount++; remove(); addBefore(lm.header); } } void recordRemoval(HashMap<K,V> m) { remove(); } }
In the put function code, we can see that the addEntry method is called, whileLinkedHashMapThat is to say, if this method is rewritten, The addBefore (header) method is called to record the sequential relationship of each element added:
void addEntry(int hash, K key, V value, int bucketIndex) { createEntry(hash, key, value, bucketIndex); // Remove eldest entry if instructed, else grow capacity if appropriate Entry<K,V> eldest = header.after; if (removeEldestEntry(eldest)) { removeEntryForKey(eldest.key); } else { if (size >= threshold) resize(2 * table.length); } } /** * This override differs from addEntry in that it doesn't resize the * table or remove the eldest entry. */ void createEntry(int hash, K key, V value, int bucketIndex) { HashMap.Entry<K,V> old = table[bucketIndex]; Entry<K,V> e = new Entry<K,V>(hash, key, value, old); table[bucketIndex] = e; e.addBefore(header); size++; }
Note that the containsValue method needs to traverse the array in HashMap:
public boolean containsValue(Object value) { if (value == null) return containsNullValue(); Entry[] tab = table; for (int i = 0; i < tab.length ; i++) for (Entry e = tab[i] ; e != null ; e = e.next) if (value.equals(e.value)) return true; return false; }
InLinkedHashMapBecause we maintain a linked list containing all elements, the length of the linked list must be less than 75 percent of the length of the array. If you want to check whether a value is in this list, just traverse this linked list:
public boolean containsValue(Object value) { // Overridden to take advantage of faster iterator if (value==null) { for (Entry e = header.after; e != header; e = e.after) if (e.value==null) return true; } else { for (Entry e = header.after; e != header; e = e.after) if (value.equals(e.value)) return true; } return false; }