1 核心知识点
- 底层数据存储结构
- 初始化容量
- 扩容机制
- 线程安全
- 时间复杂度
2 关键代码分析
- 底层数据结构
敲黑板:数组+链表+红黑树
先了解底层的数据存储结构,在HashMap内部有table属性
transient Node<K,V>[] table;
可以看到table属性是一个Node数组,那么这个Node又是怎么样一个对象呢
static class Node<K,V> implements Map.Entry<K,V> {
final int hash;
final K key;
V value;
Node<K,V> next;
Node(int hash, K key, V value, Node<K,V> next) {
this.hash = hash;
this.key = key;
this.value = value;
this.next = next;
}
Nodd是HashMap里面的一个静态内部类,里面有四个属性
hash:用来记录key的哈希值
key:记录存储元素的key
value:记录存储元素的value值
next:应用于链表数据结构,当元素数量增加的时候,如果一直保持数组结构,会影响HashMap的存储效率,所以这个元素链表结构需要记录的下一个元素的指针
- put方法详解
public V put(K key, V value) {
return putVal(hash(key), key, value, false, true);
}
这里关注一下hash(key)这个计算key的hash值的方法
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
- 判断key是不是为null,如果为null,key的hash值为0,这里说明HashMap的key是可以为null的,。
- 如果不为0,则用key的hash值和key值的右移16位的结果进行^计算。这里的原理是,hash值是存在一定概率的冲突的,而我们在计算元素在Node数组中的存储索引的时候,是通过i = (n - 1) & hash这个方式计算,这里的n就是数组的容量大小,n一般不会超过2的16次方65536,也就是1<<<16的结果,所以 (n - 1) & hash进行&计算的时候,高16位是没有参与计算的,为了降低hash冲突,于是把高16位也作为变量因子参与hash值的计算,即h = key.hashCode()) ^ (h >>> 16)。
看看关键方法putVal
fi