Java之HashMap

Java之HashMap
从这次开始我们分享一下Java里边的内容,本次我把HashMap梳理一下,虽然毛竹做Java开发时间很短,但上次有碰到问HashMap源码,一下子懵逼了,为了不让大家懵逼,我就谈谈它。
关键词:Map、HashMap、HashMap源码
1Map家族
说到Map家族,做过Java开发的同学们一定对它很了解,可是要说到它的子女大家可不一定了解,那首先上图吧,看看它有哪些子女。(刚好也把Iterator、Collection画上吧)
家里笔记本快十年的老古董了,不敢装vision,这图用WPS在线工具画的,凑合着看吧,只画了主要的哈。
在这里插入图片描述2HashMap
下面开始本次核心内容HashMap的前世今生。
2.1HashMap概念
HashMap实现了Map接口,继承AbstractMap,其中Map接口定义了键映射到值的规则,而AbstractMap类提供Map接口的骨干实现,以最大限度地减少实现此类所需的工作。
在这里插入图片描述
2.2HashMap数据结构
HashMap实际上是一个“链表散列”,数据结构如下:
实际上HashMap底层实现还是数组,只是数组的每一项都是一条链。
2.3HashMap源码
1.构造函数源码
在这里插入图片描述该构造函数中,参数initialCapacity代表了该数组的长度。
在这里插入图片描述
每次新建HashMap时,都会初始化一个table数组,table数组的元素为Entry节点。而Entry是HashMap的内部类,包含了键key、值value、下个节点next以及hash值,正是由于此种设计才构成了table数组的项为链表。
在这里插入图片描述
关于HashMap读写可以参考java提高篇(二三)-----HashMap
2.4为什么要重写hashCode()和equals()方法?
大家都知道,hashMap中,如果key为自定义类,那么就必须要重写hashCode()和equals()方法。那么这是为什么呢?
首先为什么要重写equals()方法呢?
还得看源码:
在这里插入图片描述
可以看到put方法中,判断key是否相等,用的是equals方法。其实get方法中也是一样。那么默认的equals方法就不一定能满足我们的要求了。因此要重写equals方法。
那么又为什么要重写hashCode方法呢?
这就涉及到Object.hashCode()的通用约定了:
1.每次应用执行期间,在对象做equals比较所用到的信息没有修改之前,hashcode方法的返回值应该是相同的。
2.两个对象equals方法相同,那么hashCode值必须相同;
3.两个对象equals方法不同,那么hashCode值不一定不同。
如果只重写equals方法,不重写hashCode方法,那么显然会违反第二条约定。
再具体到HashMap中,有如下源码:
在这里插入图片描述
其中if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k))))
这句明显可以看出,get的时候会先进行hash的比较,之后才会进行equals判断。上面源码分析过,hash的计算是基于hashCode()方法的。不重写的话,一般情况下hashCode()方法的返回值不同,那么就无法get到正确的key所对应的value。
如果只重写equals,不重写hashCode,那么存入key内容相同的对象时,会存入两个key内容相同的对象,而不是覆盖掉原有的对象。
取出的时候也取不出正确的对象。
因此必须要重写hashCode()和equals()方法。
2.5HashMap&HashTab
HashTable线程安全,HashMap线程不安全。
HashTable不可存储null值,HashMap可以存储null值。
HashMap去掉了HashTable的contains⽅方法,但是加上了 containsValue()和containsKey()⽅方法。
2.6HashMap线程不安全
HashMap在put的时候,插入的元素超过了容量(由负载因子决定)的范围就会触发扩容操作,就是rehash,这个会重新将原数组的内容重新hash到新的扩容数组中,在多线程的环境下,存在同时其他的元素也在进行put操作,如果hash值相同,可能出现同时在同一数组下用链表表示,造成闭环,导致在get时会出现死循环,所以HashMap是线程不安全的。
2.7常见面试题
1.当两个对象的hashcode相同会发生什么?
hashcode相同,说明两个对象HashMap数组的同一位置上,接着HashMap会遍历链表中的每个元素,通过key的equals方法来判断是否为同一个key,如果是同一个key,则新的value会覆盖旧的value,并且返回旧的value。如果不是同一个key,则存储在该位置上的链表的链头
2.如果两个键的hashcode相同,你如何获取值对象?
遍历HashMap链表中的每个元素,并对每个key进行hash计算,最后通过get方法获取其对应的值对象
3.你了解重新调整HashMap大小存在什么问题吗?
当多线程的情况下,可能产生条件竞争。当重新调整HashMap大小的时候,确实存在条件竞争,如果两个线程都发现HashMap需要重新调整大小了,它们会同时试着调整大小。在调整大小的过程中,存储在链表中的元素的次序会反过来,因为移动到新的数组位置的时候,HashMap并不会将元素放在LinkedList的尾部,而是放在头部,这是为了避免尾部遍历(tail traversing)。如果条件竞争发生了,那么就死循环了
好了,本期就到这里,这次确实拖了很久,以后还是尽量少拖延吧,下次分享Maven。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值