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方法源码
1. public V put(K key, V value) {
2. // 如果key为null使用putForNullKey来获取
3. if (key == null)
4. return putForNullKey(value);
5. // 使用hash函数预处理hashCode
6. int hash = hash(key.hashCode());
7. // 获取对应的索引
8. int i = indexFor(hash, table.length);
9. // 得到对应的hash值的桶,如果这个桶不是,就通过next获取下一个桶
10. for (Entry<K,V> e = table[i]; e != null; e = e.next) {
11. Object k;
12. // 如果hash相同并且key相同
13. if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
14. // 获取当前的value
15. V oldValue = e.value;
16. // 将要存储的value存进去
17. e.value = value;
18. this);
19. // 返回旧的value
20. return oldValue;
21. }
22. }
23.
24. modCount++;
25. addEntry(hash, key, value, i);
26. return null;
27. }
hashmap新增一个键值对时,先计算key的hashcode值计算它在entry[]数组中的位置,如果位置没有被占用,直接把entry放在这个位置,如果这个位置上已经有值了,然后把当前位置的entry类的key,value变量值变为新添加的键值对,而next的entry放置老的key,value,而next再次放下一个的next的entry类的key,value,通过for循环逐渐附值。