HashMap概述
HashMap实现了Map接口,我们常用HashMap进行put和get操作读存键值对数据。下面介绍基于jdk1.8深入了解HashMap底层原理。
开始之前,记得点赞收藏加关注哦 ,需要下载PDF版本和获取更多知识点、面试题的朋友可以点一点下方链接免费领取
链接:点这里!!! 799215493 暗号:CSDN
HashMap数据结构
HashMap实际是一种“数组+链表”数据结构。在put操作中,通过内部定义算法寻止找到数组下标,将数据直接放入此数组元素中,若通过算法得到的该数组元素已经有了元素(俗称hash冲突,链表结构出现的实际意义也就是为了解决hash冲突的问题)。将会把这个数组元素上的链表进行遍历,将新的数据放到链表末尾。
存储数据的对象
static class Node<K,V> implements Map.Entry<K,V> {
final int hash;
final K key;
V value;
Node<K,V> next;
}
我们从jdk1.8源代码看出存储对象Node实际是实现Map.Entry对象接口。
hash:通过hash算法的出来的值。hash值的算法我们看下HashMap源代码的实现
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
不同的数据类型的hashCode计算的方法不一样,我们看下String和Integer两种数据类型的hashCode算法
String.hashCode()
public int hashCode() {
int h = hash;
if (h == 0 && value.length > 0) {
char val[] = value;
for (int i = 0; i < value.length; i++) {
h = 31 * h + val[i];
}
hash = h;
}
return h;
}
通过将字符串转换成char数组,使用公式s[0]*31^(n-1) + s[1]*31^(n-2) + … + s[n-1]进行计算得出最后的值。val[i]值是对应字符的ASCII值.在看到这里的时候,这里为什么使用了一个31作为相乘因子(能为啥,还不是为了性能考虑,那为什么使用31性能能得到优化呢),这里可以延伸讨论。
Integer.hashCode()
public static int hashCode(int value) {
return value;
}
直接返回值.
key:存储数据的key
value:存储数据的value
next:下一个数据,出现哈希冲突时,该数组元素会出现链表结构,会使用next指向链表中下一个元素
对象链表结构导致的问题
通过哈希算法从寻止上能够高效的找到对应的下标,但是随着数据的增长,哈希冲突碰撞过多。在寻找数据上,找到该来链表,会通过遍历在寻找对应数据,如此将会使得get数据效率越来越低。在jdk1.8中,链表元素数量大于等于8将会重组该链表结构形成为“红黑树结构”,这种结构使得在hash冲突碰撞过多情况下,get效率比链表的效率高很多。
HashMap put存储数据是如何处理的
HashMap有几个重要的变量
transient Node<K,V>[] table;
int threshold;
final float loadFactor;
int modCount;
int size;
table:存储数组的变量,初始长度为16通过源代码看出在第一次进行resize扩容(java是静态语言,在定义数组初始化时,需要定义数组的长度,在map数据增长后,内部机制会进行重新定义一个数组做到扩容的操作)初始化时,会将默认静态变量
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4;
赋给数组长度进行初始化。
loadFactor:数据的增长因子,默认为0.75。在进行扩容操作会使用到。
threshold:允许的最大的存储的元素数量,通过length数组长度*loadFactor增长因子得出
modCount:记录内部结构发生变化的次数,put操作(覆盖值不计算)以及其他…
size:实际存储的元素数量
put的流程
直接通过源代码分析
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
boolean evict) {
Node<K,V>[] tab; Node<K,V> p; int n, i;
// 判断数组是否为空,长度是否为0,是则进行扩容数组初始化
if ((tab = table) == null || (n = tab.length)