备注:下面出现的代码都是HashMap.java中的源码,中文描述是作者加的。
transient Entry[] table;//HashMap内部定义的数据存储变量 //内部类 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 V put(K key, V value) { if (key == null) //如果key为null,特殊处理,key为null直接存储在table[0]位置。 return putForNullKey(value); int hash = hash(key.hashCode()); int i = indexFor(hash, table.length);//此处得到的i即为key对应的HashMap中的存储位置table[i] for (Entry<K,V> e = table[i]; e != null; e = e.next) { //从Entry链表的第一个开始如果找到与key执行equals方法为true的Entry,则修改对应Entry的value值为新值,key不做修改 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++; //如果没有找到对应的key,则执行增加操作 addEntry(hash, key, value, i); return null; } 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) //如果大小超过了threshold,扩大容量为原来的两倍。扩大容量时,所有的key-value需要重新hash。 resize(2 * table.length); } void resize(int newCapacity) { Entry[] oldTable = table; int oldCapacity = oldTable.length; if (oldCapacity == MAXIMUM_CAPACITY) { threshold = Integer.MAX_VALUE; return; } Entry[] newTable = new Entry[newCapacity]; transfer(newTable);//将原来hash表中的数据放入新的hash表中,需要重新hash。 table = newTable; threshold = (int)(newCapacity * loadFactor); } /** * Transfers all entries from current table to newTable. */ void transfer(Entry[] newTable) { Entry[] src = table; int newCapacity = newTable.length; for (int j = 0; j < src.length; j++) { Entry<K,V> e = src[j]; if (e != null) { src[j] = null; //此处使用循环,将原来hash链中的所有的key-value都重新获取hash值,重新放置。 //因为放置位置是跟hash表的大小有关的,当hash表容量扩大后,之前放在一个地方的key-value对现在可能hash不到同一个地方了。 do { Entry<K,V> next = e.next;//记录此处的下一个地址 int i = indexFor(e.hash, newCapacity);//重新计算当前的key-value在新hash表中的位置 e.next = newTable[i];//将之前在同一位置的数据放在e的next位置,没有则为null newTable[i] = e;//将e作为hash表i位置的第一个元素 e = next;//将next赋值给e, 对原来j位置的所有的元素都执行重新hash,重新放置 } while (e != null); } } }
这两个方法作用好理解,但需要注意的是,当对keySet()和values()方法获取到的集合执行remove操作的时候就相当于对HashMap集合本身执行remove操作。看源码通过keySet和values获取到的好像是HashMap的迭代器,这里我没有深究。如果谁明白具体原因不吝赐教。