概念
HashMap是java中一种非常常用的基于哈希表的数据结构,允许o(1)
的时间复杂度进行元素插入,查找,和删除。它通过”键-值“ 对的方式存储数据。
总的来说:HashMap的底层原理:数组+链表+红黑树(jdk1.8之后还涉及红黑树)。
哈希函数与哈希值:每个键都会通过哈希函数计算哈希值,然后通过哈希值决定数组在那个桶(buxket)中。桶是一个数组的存储位置。
数组:hashmap底层是一个数组,每个数组元素存放一个链表或者红黑树(1.8之后)
当新元素插入hashmap时,它首先根据哈希值找到数组中的某个位置(桶)。如果该位置为空,则
则直接插入;如果该位置已经存在了元素(发生碰撞),链表或红黑树解决冲突。
hash冲突——链表和红黑树:
如果发生哈希冲突,hashMap会将相同的哈希值的元素以链表的形式存储在一个桶中(数组的某个位置)。
当链表长度过长时候,时间复杂度变为O(n).当链表长度超过一定的阈值(默认是8)时,链表会转换为红黑树,从而将时间复杂度从O(n)降低到O(log n).
负载因子和扩容:
Hash Map有一个重要的参数叫负载因子,它决定了当数组中元素数量超过数组容量的多大比例时,会触发扩容操作。默认负载因子是0.75,当HashMap的元素数量达到数组容量的75%时,HashMap会自动扩容,通常会将数组容量扩展为原来的2倍。扩容时,HashMap会重新分配一个更大的数组,并将原来的数组映射到新的数组中,这个过程叫做rehashing。过程比较耗时,因为要重新计算每个元素的哈希值,并将其放入桶中。
源码分析:
HashMap 的默认初始化容量是 16,负载因子是 0.75。
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
static final float DEFAULT_LOAD_FACTOR = 0.75f;
HashMap 的 put 方法是插入元素的核心逻辑。
hash() 方法计算键的哈希值。为了减少哈希冲突,它通过异或运算将高位信息与低位结合,混合高位与低位的位信息.
public V put(K key, V value) {
return putVal(hash(key), key, value, false, true);
}
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}