We can look at jdk7 HashMap source code to understand how HashMap is constructed. Jdk7 HashMap implementation is simplier to understand than the newer versions.
Empty parameter constructor creates a HashMap with default initial capacity 16 and default load factor 0.75.
Default Constructor:
1 2 3 4 5 6 7 8 9 10
/** * Constructs an empty <tt>HashMap</tt> with the default initial capacity * (16) and the default load factor (0.75). */ publicHashMap() { this.loadFactor = DEFAULT_LOAD_FACTOR; threshold = (int)(DEFAULT_INITIAL_CAPACITY * DEFAULT_LOAD_FACTOR); table = newEntry[DEFAULT_INITIAL_CAPACITY]; init(); }
note that init() is an Initialization hook for subclasses.
/** * Associates the specified value with the specified key in this map. * If the map previously contained a mapping for the key, the old * value is replaced. * * @param key key with which the specified value is to be associated * @param value value to be associated with the specified key * @return the previous value associated with <tt>key</tt>, or * <tt>null</tt> if there was no mapping for <tt>key</tt>. * (A <tt>null</tt> return can also indicate that the map * previously associated <tt>null</tt> with <tt>key</tt>.) */ public V put(K key, V value) { if (key == null) return putForNullKey(value); inthash= hash(key.hashCode()); inti= indexFor(hash, table.length); 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))) { VoldValue= e.value; e.value = value; e.recordAccess(this); return oldValue; } }
put() method calls addEntry() if the key is not found in the table.
1 2 3 4 5 6 7 8 9 10 11 12 13
/** * Adds a new entry with the specified key, value and hash code to * the specified bucket. It is the responsibility of this * method to resize the table if appropriate. * * Subclass overrides this to alter the behavior of put method. */ voidaddEntry(int hash, K key, V value, int bucketIndex) { Entry<K,V> e = table[bucketIndex]; table[bucketIndex] = newEntry<>(hash, key, value, e); if (size++ >= threshold) resize(2 * table.length); }
indexFor() method is used to calculate the index in the table for the hash code.
1 2 3 4 5 6
/** * Returns index for hash code h. */ staticintindexFor(int h, int length) { return h & (length-1); }
putForNullKey is a special method to handle null key. It is trying to insert the value to the first entry in the table.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
/** * Offloaded version of put for null keys */ private V putForNullKey(V value) { for (Entry<K,V> e = table[0]; e != null; e = e.next) { if (e.key == null) { VoldValue= e.value; e.value = value; e.recordAccess(this); return oldValue; } } modCount++; addEntry(0, null, value, 0); returnnull; }
get() method
get method is used to get the value of a key.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
/** * Returns the value to which the specified key is mapped, * or {@code null} if this map contains no mapping for the key. */ public V get(Object key) { if (key == null) return getForNullKey(); inthash= hash(key.hashCode()); for (Entry<K,V> e = table[indexFor(hash, table.length)]; e != null; e = e.next) { Object k; if (e.hash == hash && ((k = e.key) == key || key.equals(k))) return e.value; } returnnull; }
getForNullKey
1 2 3 4 5 6 7 8 9 10 11 12 13 14
/** * Offloaded version of get() to look up null keys. Null keys map * to index 0. This null case is split out into separate methods * for the sake of performance in the two most commonly used * operations (get and put), but incorporated with conditionals in * others. */ private V getForNullKey() { for (Entry<K,V> e = table[0]; e != null; e = e.next) { if (e.key == null) return e.value; } returnnull; }
/** * Rehashes the contents of this map into a new array with a * larger capacity. This method is called automatically when the * number of keys in this map reaches its threshold. * * If current capacity is MAXIMUM_CAPACITY, this method does not * resize the map, but sets threshold to Integer.MAX_VALUE. * This has the effect of preventing future calls. * * @param newCapacity the new capacity, MUST be a power of two; * must be greater than current capacity unless current * capacity is MAXIMUM_CAPACITY (in which case value * is irrelevant). */ voidresize(int newCapacity) { Entry[] oldTable = table; intoldCapacity= oldTable.length; if (oldCapacity == MAXIMUM_CAPACITY) { threshold = Integer.MAX_VALUE; return; }
/** * Transfers all entries from current table to newTable. */ voidtransfer(Entry[] newTable) { Entry[] src = table; intnewCapacity= newTable.length; for (intj=0; j < src.length; j++) { Entry<K,V> e = src[j]; if (e != null) { src[j] = null; do { Entry<K,V> next = e.next; inti= indexFor(e.hash, newCapacity); e.next = newTable[i]; newTable[i] = e; e = next; } while (e != null); } } }
clear() method
1 2 3 4 5 6 7 8 9 10 11 12 13
/** * Removes all of the mappings from this map. * The map will be empty after this call returns. */ publicvoidclear() { Node<K,V>[] tab; modCount++; if ((tab = table) != null && size > 0) { size = 0; for (inti=0; i < tab.length; ++i) tab[i] = null; } }
/** * Returns {@code true} if this map contains a mapping for the * specified key. * * @param key The key whose presence in this map is to be tested * @return {@code true} if this map contains a mapping for the specified * key. */ publicbooleancontainsKey(Object key) { return getNode(key) != null; }
/** * Implements Map.get and related methods. * * @param key the key * @return the node, or null if none */ final Node<K,V> getNode(Object key) { Node<K,V>[] tab; Node<K,V> first, e; int n, hash; K k; if ((tab = table) != null && (n = tab.length) > 0 && (first = tab[(n - 1) & (hash = hash(key))]) != 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 (first instanceof TreeNode) return ((TreeNode<K,V>)first).getTreeNode(hash, key); do { if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k)))) return e; } while ((e = e.next) != null); } } returnnull; }
/** * Returns {@code true} if this map maps one or more keys to the * specified value. * * @param value value whose presence in this map is to be tested * @return {@code true} if this map maps one or more keys to the * specified value */ publicbooleancontainsValue(Object value) { Node<K,V>[] tab; V v; if ((tab = table) != null && size > 0) { for (Node<K,V> e : tab) { for (; e != null; e = e.next) { if ((v = e.value) == value || (value != null && value.equals(v))) returntrue; } } } returnfalse; }