java 1.7 hashmap,HashMap实现原理(jdk1.7/jdk1.8)

HashMap的底层实现:

1、简单回答

JDK1.7:HashMap的底层实现是:数组+链表

JDK1.8:HashMap的底层实现是:数组+链表/红黑树

为什么要红黑树?

红黑树:一个自平衡的二叉树

当结点多了用红黑树,少了用链表

因为少的话用红黑树太复杂,多了话用红黑树可以提高查询效率。

红黑树:(自动调整根结点,保证左右两边的结点数差不多),它会左旋,右旋来实现。

JDK1.7:HashMap的底层实现是:数组+链表

2、复杂回答v1.0版

每一个对映射关系的存储位置:

存储的位置:

(1)先计算key的hash值,通过hashCode()就可以得到,

(2)再用hash值经过各种(异或)操作,得到一个int数字,目的是使得更均匀的分布在数组的每一个元素中。

(3)再用刚才的int值,与table数组的长度-1做一个“按位与&"

运算,目的是得到一个[i]

因为数组的长度是2的n次方,长度-1的数的二进制是前面都是0,后面都是1,任何数与它做“&”,结果一定是[0,该数]之间

为什么会出现链表?

(1)两个不同的key对象,仍然可能出现hashCode值一样

(2)就算hashCode不一样,经过刚才的计算,得到[i]是一样的

(3)而我们的数组的元素table[i]本来只能放一个元素,那么现在有多个元素需要存储到table[i]中,只能把这几个元素用链表连接起来

简单比喻:

y = f(x)

两个不一样的x,可能得到一样的y

那么存储到HashMap中的是什么样的元素呢?

(1)首先是Map.Entry类型:映射项(键-值对)。

(2)其次HashMap有一个内部类HashMap.Entry实现了Map.Entry接口类型

内部类Entry由四部分组成:

(1)key

(2)value

(3)下一个Entry:next

(4)hash值计算的整数值,为了后面查询快一点

如何避免重复?

如果两个key的hash值是一样的,还要调用一下equlas()方法,如果返回true,就会把新的value替换旧的value。

如果两个key的hash值是一样的,还要调用一下equlas()方法,如果返回false,就会把新的Entry连接到旧的Entry所在链表的头部(first)

如果两个key的hash值是不一样的,但是[i]是一样的,就会把新的Entry连接到旧的Entry所在链表的头部(first)

如果两个key的hash值是不一样的,并且[i]不一样的,肯定存在不同table[i]中

我们把table[i]称为“桶bucket”。

回忆:两个对象的hash值:

(1)如果两个对象是“相等”,他们的hash值一定是一样

(2)如果两个对象的hash值是一样,他们可能是相同的对象,也可能是不同的对象。

3、复杂追踪源代码v2.0版

(1)什么时候扩容

当元素的个数达到“阈值”,并且新添加的映射关系计算出来的table[i]位置是非空的情况下,再扩容

默认加载因子 (0.75):DEFAULT_LOAD_FACTOR

阈值 = table.length * 加载因子(loadFactor)

第一次阈值:16 * 0.75 = 12

初始容量:DEFAULT_INITIAL_CAPACITY:16

第二次阈值:32 * 0.75 = 24

...

HashMap中table的长度有一个要求:必须是2的n次方

(2)跟踪一下put方法的源代码

第一步:如果table是空的,会先把table初始化为一个长度为16的数组,如果你指定的长度不是2的n次方,会往上纠正为最接近的2的n次方

并且把阈值 threshold= table.length * 加载因子(loadFactor) = 12。

第二步:如果key是null,首先确定的位置是table[0],

如果原来table[0]已经有key为null的Entry,用新的value替换旧的value

如果原来table[0]没有key为null的Entry,那么创建一个新的Entry对象,作为table[0]桶下面的链表的头,原来的那些作为它next。

第三步:如果key不是null,那么用key的hashCode值,通过hash()函数算出一个int值称为"hash"

第四步:通过刚才的“hash”的int值与table.length-1做&运算,得到一个下标index,表示新的映射关系即将存入table[index]

第五步:循环判断table[index]位置是否为空,并且是否有Entry的key与我新添加的key是否“相同”,如果相同,就用新的value替换旧的value

第六步:添加新的映射关系

(1)判断是否要扩容:

当元素的个数达到“阈值”,并且新添加的映射关系计算出来的table[i]位置是非空的情况下,table再扩容为原来的2倍长

如果扩容了,那么要重新计算hash和index

(2)把新的映射关系创建为一个Entry的对象放到table[index]的头部。

JDK1.8:HashMap的底层实现是:数组+链表/红黑树

1、复杂回答v1.0

(1)映射关系的类型

添加到HashMap1.8种的映射关系的对象是HashMap.Node类型,或者是HashMap.TreeNode类型。

它也是Map.Entry接口的实现类。

(2)映射关系添加到table[i]中时,如果里面是链表,新的映射关系是作为原来链表的尾部

“七上八下”:JDK1.7在上,JDK1.8在下。

为什么要在下面,因为如果是红黑树,那么是在叶子上,保持一致,都在下面。

(3)扩容的时机不同

第一种扩容:元素个数size达到阈值threshod = table.length * 加载因子  并且table[i]是非空的

第二种扩容:当某个桶table[index]下面的结点的总数原来已经达到了8个,这个时候,要添加第9个时,会检查

table.length是否达到64,如果没达到就扩容。如果添加第10个时,也会检查table.length是否达到64,如果没达到就扩容。

为什么,因为一旦扩容,所有的映射关系要重新计算hash和index,一计算原来table[index]下面可能就没有8个,或新的映射关系也可能不在table[index],

这样就可能均匀分布。

(4)什么时候从链表变成红黑树

当table[index]下面结点的总数已经达到8个,并且table.length也已经达到64,那么再把映射关系添加到

table[index]下时,就会把原来的链表修改红黑树

(5)什么时候会从红黑树变为链表

当删除映射关系时table[index]下面结点的总数少于6个,会把table[index]下面的红黑树变回链表。

2、put的源代码v2.0

第一步:计算key的hash,用了一个hash()函数,目的得到一个比hashCode更合理分布的一个int值

第二步:如果table是空的,那么把table初始化为长度为16的数组,阈(yu)值初始化为= 默认的长度16 * 默认的加载因子0.75 = 12

DEFAULT_INITIAL_CAPACITY:默认的初始化容量16

DEFAULT_LOAD_FACTOR:默认加载因子 0.75

第三步:查看table[index]是否为空,如果为空,就直接new一个Node放进去

index = hash的int值 & table.length-1

第四步:先查看table[index]的根节点的key是否与新添加的映射关系的key是否“相同”,如果相同,就用新的value替换原来的value。

第五步:如果table[index]的根节点的key与新添加的映射关系的key不同,

还要看table[index]根结点的类型是Node还是TreeNode类型,

如果是Node类型,那么就查看链表下的所有节点是否有key与新添加的映射关系的key是否“相同”,如果相同,就用新的value替换原来的value。

如果是TreeNode类型,那么就查看红黑树下的所有叶子节点的key是否与新添加的映射关系的key是否“相同”,如果相同,就用新的value替换原来的value。

第六步:如果没有找到table[index]有结点与新添加的映射关系的key“相同”,那么

如果是TreeNode类型,那么新的映射关系就创建为一个TreeNode,连接到红黑树中

如果是Node类型,那么要查看链表的结点的个数,是否达到8个,如果8个,并且table.length小于64,那么先扩容。

TREEIFY_THRESHOLD:树化阈值8

MIN_TREEIFY_CAPACITY:最小树化容量64

UNTREEIFY_THRESHOLD:反树化,从数变为链表的阈值6

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值