概述
HashMap实现了Map接口,也是以键值对(K-V)的形式的一种数据结构,其内部由数组+链表/红黑树实现。
特点
以键值对(K-V)存储
不保证顺序
查询速度快
允许使用 null 做键和值
非线程安全
为什么速度快
HashMap查询速度很快,是因为它由散列的方法来计算元素的存储位置和元素的查找。上面提到,HashMap是数组+链表/红黑树实现的。简单来说就是,创建HashMap对象时,内部会初始化一个数组,添加元素时根据元素的Key,通过特殊算法计算出一个 int 类型的散列码,这个散列码其实就是该元素要存储在数组上的位置(数组下标)。
数组:是用来存储键的信息(注意,是键的信息,不是存键本身),为什么不直接存键本身呢?因为数组不能调整容量,但又希望Map中保持数量不确定的的值,让不同的键可以产生相同的下标(也就是说,可能会有冲突)。多个键产生同个下标后,会以链表的形式存储:
查询一个值得过程,首先要根据 Key 计算出散列码,然后根据散列码查询数组,如果这个时候没有冲突,那是最完美的,不用再进一步查询链表,如果有冲突,那就要使用 equals() 方法进行线性查询,可以看出,冲突越多,查找性能会越差。所以 Jdk8 中做了优化,当链表长度超过 8 的时候,用红黑树的结构存储(红黑树这里不做细说)。
这种查询值得方式不需要线性的遍历所有元素,而是通过散列码快速定位到数组的某个位置,只对很少的元素进行比较,这就是HashMap如此快的原因。
jdk8 put、get、remove源码解析(jdk8)
put
public V put(K key, V value) {
// 对key计算 hash值(散列码),然后传递给 putVal 方法
return putVal(hash(key), key, value, false, true);
}
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
boolean evict) {
Node<K,V>[] tab; Node<K,V> p; int n, i;// p 可以理解为数组该位置链表的第一个元素
// 判断数组是否为空,如果为空。
if ((tab = table) == null || (n = tab.length) == 0)
// 调用 resize() 初始化创建
n = (tab = resize()).length;
// 散列码取模得到数组下标 i,并判断该位置是否为 null
if ((p = tab[i = (n - 1) & hash]) == null)
// 说明还没有元素被散列到此(该位置没冲突&#x