hashmap是基于hash算法实现的一个存储键值对的大型hash表,他本质上是一个entry类的数组,entry[],entry是hashmap的内部类,之所有使用内部类,是因为内部类可以使用所有的外部类所有变量,并且只用于外部类,期望对其他类不可见。我们来看一看entry的源码
static class Entry<K,V> implements Map.Entry<K,V> { final K key; V value; Entry<K,V> next; final int hash; // 构造函数 Entry(int h, K k, V v, Entry<K,V> n) { value = v; next = n; key = k; hash = h; } // 返回key public final K getKey() { return key; } // 返回value public final V getValue() { return value; } // 设置value public final V setValue(V newValue) { V oldValue = value; value = newValue; return oldValue; } // 是否相同 public final boolean equals(Object o) { // 如果o不是Map.Entry的实例,那么肯定不相同了 if (!(o instanceof Map.Entry)) return false; // 将o转成Map.Entry Map.Entry e = (Map.Entry)o; // 得到key和value对比是否相同,相同则为true 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; } // 否则为false return false; } // hashCode public final int hashCode() { return (key==null ? 0 : key.hashCode()) ^ (value==null ? 0 : value.hashCode()); } // 返回String public final String toString() { return getKey() + "=" + getValue(); } // 使用该方法证明该key已经在该map中 void recordAccess(HashMap<K,V> m) { } // 该方法记录该key已经被移除了 void recordRemoval(HashMap<K,V> m) { } } // 添加一个新的桶来保存该key和value void addEntry(int hash, K key, V value, int bucketIndex) { // 保存对应table的值 Entry<K,V> e = table[bucketIndex]; // 然后用新的桶套住旧的桶,链表 table[bucketIndex] = new Entry<K,V>(hash, key, value, e); // 如果当前size大于等于阈值 if (size++ >= threshold) // 调整容量 resize(2 * table.length); } // 新建一个桶,该方法不需要判断是否超过阈值 void createEntry(int hash, K key, V value, int bucketIndex) { Entry<K,V> e = table[bucketIndex]; table[bucketIndex] = new Entry<K,V>(hash, key, value, e); size++; }看到源码就清楚了,其实map其实是用的entry类的变量key,value分别用来存储键值对,然后next用于存储hash值相同的entry对象。看了源码这么久,我觉得hashmap最精彩的一部分就是利用key的hashcode来规定entry对象在entry[]里的位置,当两个key的hashcode相同时,当前数组的位置由于存储最新的entry类,然后用next存储老的entry,而这个老的entry类的next变量又可能存储一个entry类,这样不断递归就形成了链表。
hashmap重写了超类object中的hashmap,equal,tostring方法。因为key值是唯一的,而相同key的hashcode的值可能不同,需要我们重写.
我们再来看一看hashmap的put方法源码
public V put(K key, V value) { // 如果key为null使用putForNullKey来获取 if (key == null) return putForNullKey(value); // 使用hash函数预处理hashCode int hash = hash(key.hashCode()); // 获取对应的索引 int i = indexFor(hash, table.length); // 得到对应的hash值的桶,如果这个桶不是,就通过next获取下一个桶 for (Entry<K,V> e = table[i]; e != null; e = e.next) { Object k; // 如果hash相同并且key相同 if (e.hash == hash && ((k = e.key) == key || key.equals(k))) { // 获取当前的value V oldValue = e.value; // 将要存储的value存进去 e.value = value; e.recordAccess(this); // 返回旧的value return oldValue; } } modCount++; addEntry(hash, key, value, i); return null; }hashmap新增一个键值对时,先计算key的hashcode值计算它在entry[]数组中的位置,如果位置没有被占用,直接把entry放在这个位置,如果这个位置上已经有值了,然后把当前位置的entry类的key,value变量值变为新添加的键值对,而next的entry放置老的key,value,而next再次放下一个的next的entry类的key,value,通过for循环逐渐附值。