谈谈JDK1.8和1.7中的HashMap的底层原理

1.HashMap中的每个元素都是key、value键值对

1.8中用Node<K,V>表示,它是HashMap的静态内部类,实现接口 Map.Entry<K,V>,且有一个next指向下一个节点
在这里插入图片描述

2.HashMap的结构是: 数组+(链表或红黑树)

为什么采用这种结构来存储元素呢?
数组的特点:查询效率高,插入,删除效率低。
链表的特点:查询效率低,插入删除效率高。
在HashMap底层使用数组加(链表或红黑树)的结构完美的解决了数组和链表的问题,使得查询和插入,删除的效率都很高。

PS: 数组加(链表或红黑树) 其实就是 数组加链表数组加红黑树搭配使用。因为,随着不断的往map中添加元素,数据中的链表中元素会逐渐增多,长度太大在查找元素时会花更多的时间,影响效率,因此Hashmap中设定当数组长度小于64的时候,会先选择扩容,当数组长度扩容到大于64且链表长度大于8的时候才会转红黑树红黑树是平衡二叉树,在查找性能方面比链表要高.
在这里插入图片描述
在这里插入图片描述
PS: TreeNode<K,V>是静态内部类,且被final修饰

3.HashMap插入元素

计算出 key的hashcode,该值用来定位要将这个元素存放到数组中的什么位置.
hashcode是一个本地方法,被native修饰,所谓本地方法就是非java代码,这个代码通常用c或c++写成。调用hashcode方法会生成一个int型的整数,我们叫它哈希码,哈希码和调用它的对象地址和内容有关。

哈希码的特点:
对于同一个对象,它的hashcode值都是相同的。不同对象如果他们的equals返回false,那么他们的hashcode值也有可能相等。通过hashcode值和数组长度取模我们可以得到元素存储的下标

拿到了元素可以存储的位置,接下来就是存放元素,分两种情况:

  1. 数组目标索引位置为空,这种情况很简单,直接将元素放进去就好了。
  2. 目标索引位置已有元素占据,这种情况下我们需要判断一下该位置的元素和当前元素是否相等,使用equals来比较。如果两者相等则直接覆盖,如果不等则在原元素下面使用链表的结构存储该元素。

4. HashMap扩容

HashMap中有两个重要的参数:初始容量大小加载因子
初始容量是创建时给数组分配的容量大小,默认值为16,
在这里插入图片描述

加载因子: 默认的加载因子大小是0.75,当你这样new HashMap<>()时,使用的就是默认的加载因子
在这里插入图片描述
也可以在new HashMap时指定大小,如new HashMap<>(16,0.8f),这里是指定初始化容量为16,加载因子为0.8;

扩容: 数组容量乘以加载因子得到一个值,一旦数组中存储的元素个数超过该值,就会调用rehash方法将数组容量增加到原来的两倍。

由于在计算中位运算比取模运算效率高的多,所以 HashMap 规定数组的长度为 2^n 。这样用 2^n - 1 做位运算与取模效果一致,并且效率还要高出许多。
: 取模运算 (a % b) 在当b为2^n时可简化为 a & (b-1)

在做扩容的时候会生成一个新的数组,原来的所有数据需要重新计算哈希码值重新分配到新的数组,所以扩容的操作非常消耗性能。

并发环境下 HashMap 容易出现死循环
并发场景发生扩容,调用 resize() 方法里的 rehash() 时,容易出现环形链表。这是因为 此时 HashMap 的结构就会发生翻天覆地的变化,并把数据从老的 Hash 表中迁移到新的 Hash 表中。
试想,有两个线程同时进行扩容操作,其中一个线程在遍历到节点e1时挂起,去干其它的事了,此时e1指向e2节点;但另一个线程这个过程完成了Hashmap的接口重构, 原来的 e2 成为了某个位置的表头,它指向e1。
线程一接着工作。把 e2摘下来,放到 newTable[i] 的第一个,然后往下移。这个元素所在的位置上已经存放有其他元素了,那么在同一个位置上的元素将以链表的形式存放。新加入的放在链头,而先前加入的放在链尾。

看看jdk1.7中的HashMap

1.7的HashMap的key、value是用Entry<K,V>存储的
在这里插入图片描述

对比:
在大体的属性jdk1.8和jdk1.7基本一致,最大的区别就是jdk1.8的hashmap对查询做了优化,jdk1.7因为使用链表,查询时间复杂度是O(n),而jdk1.8替换成了红黑树,时间复杂度是O(log(n))。

参考
https://zhuanlan.zhihu.com/p/79507868
https://www.cnblogs.com/warehouse/p/9418156.html

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值