HashMap原理?

1、什么是HashMap?

  • HashMap是一个散列桶(数组和链表),它存储的内容是键值对(key-value)映射
  • HashMap采用了数组和链表的数据结构,能在查询和修改方便继承了数组的快速随机查找和链表的修改优点
  • HashMap是非synchronized(线程不安全的),所以HashMap操作效率很快
  • HashMap可以接受null键和值,而Hashtable则不能(原因就是使用equlas()方法需要对象,因为HashMap是后出的,API是经过处理的,因此可以用)
  • 加载因子: 为了降低哈希冲突的概率,默认当HashMap中的键值对达到数组大小的75%时,即会触发扩容。
  • 空间换时间: 如果希望加快Key查找的时间,还可以进一步降低加载因子,加大初始大小,以降低哈希冲突的概率。

2、HashMap的工作原理是什么?

  • HashMap是基于hash的原理,我们使用put(key, value)存储对象到HashMap中,使用get(key)从HashMap中获取对象。当我们给put()方法传递键和值时,我们先对键调用hashCode()方法,计算并返回的hashCode是用于找到Map数组的bucket位置来储存Node 对象。这里关键点在于指出 HashMap是在bucket中储存键对象和值对象,作为Map.Node 。
    在这里插入图片描述

  • 以下是HashMap初始化 ,简单模拟数据结构
    Node[] table=new Node[16] 散列桶初始化,tableclass Node { hash;//hash值 key;//键 value;//值 node next;//用于指向链表的下一层(产生冲突,用拉链法)}

  • 以下是具体的put过程(JDK1.8版)

1、对Key求Hash值,计算出key应该放的下标

2、如果没有碰撞,直接放入桶中(碰撞的意思是计算得到的Hash值相同,需要放到同一个bucket中)

3、如果碰撞了,使用equals判断key是否是同一个对象。如果equals为true,则替换旧值;若为false,则以链表的方式链接到后面。

4、如果链表长度超过阀值( TREEIFY THRESHOLD==8),就把链表转成红黑树,链表长度低于6,就把红黑树转回链表

5、如果桶满了(容量16*负载因子0.75),就需要 resize(扩容2倍后重排)。

  • 以下是具体get过程(考虑特殊情况如果两个键的hashcode相同,你如何获取值对象?)
    当我们调用get()方法,HashMap会使用键对象的hashcode找到bucket位置,找到bucket位置之后,会调用keys.equals()方法去找到链表中正确的节点,最终找到要找的值对象。在这里插入图片描述
    3、有什么方法可以减少碰撞?
  • 扰动函数可以减少碰撞,原理是:如果两个不相等的对象返回不同的hashcode的话,那么碰撞的几率就会小些,这就意味着存储链表结构减小,这样取值的话就不会频繁调用equal方法,这样就能提高HashMap的性能。(扰动:即Hash方法内部的算法实现,目的是让不同对象返回不同hashcode)
  • 使用不可变的final的对象,并且采用合适的equals()和hashCode()方法
    • 因为final不可变性,键对应的hash值就是不变的;而且合适的equals()和hashCode()方法也可以保证不可变对象多次hash的值一致。否则要是key对象可变,变化后的key可能会跟表中已有的key的hash值一样,造成碰撞;而且键值在放入时和取出时返回的hashcode不同的话,就获取不HashMap中的你想要的对象。而且不可变的key可以使得key能够缓存自己的hashcode,用的时候就不需要再次计算,这将提高整个获取对象的速度。如:使用String,Interger这样的包装类作为键是非常好的选择。
      4、HashMap中hash函数怎么是是实现的?
  • hash函数就是根据一个key,算出一个hash值,从而确定key在桶中存放的位置。
  • hash函数需要保证:计算出的元素的位置分布相对来说是比较均匀的;这样找的时候就更快速,消耗资源更少。
  • 尽量使得每个位置上的元素数量只有一个,那么当我们用hash算法求得这个位置的时候,马上就可以知道对应位置的元素就是我们要的,而不用再去遍历链表。

5、拉链法导致的链表过深问题为什么不用二叉查找树代替,而选择红黑树?为什么不一直使用红黑树?

  • 之所以选择红黑树是为了解决二叉查找树的缺陷,二叉查找树在特殊情况下会变成一条线性结构(这就跟原来使用链表结构一样了,造成很深的问题)遍历查找会非常慢。
  • 引入红黑树就是为了查找数据快,解决链表查询深度的问题。当长度大于8的时候,会使用红黑树,如果链表长度很短的话,根本不需要引入红黑树,引入反而会慢。

6、说说你对红黑树的见解?
在这里插入图片描述

1、每个节点非红即黑

2、根节点总是黑色的

3、如果节点是红色的,则它的子节点必须是黑色的(反之不一定)

4、每个叶子节点都是黑色的空节点(NIL节点)

5、从根节点到叶节点或空子节点的每条路径,必须包含相同数目的黑色节点(即相同的黑色高度

7、解决hash 碰撞还有那些办法?
![在这里插入图片描述](https://img-blog.csdnimg.cn/20190827130226674.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM4NzM3NTg2,size_16,color_FFFFFF,t_70在这里插入图片描述
在这里插入图片描述
在这里插入图片描述在这里插入图片描述
8、如果HashMap的大小超过了负载因子(load factor)定义的容量,怎么办?

  • 默认的负载因子大小为0.75,也就是说,当一个map填满了75%的bucket时候,和其它集合类(如ArrayList等)一样,将会创建原来HashMap大小的两倍的bucket数组,来重新调整map的大小,并将原来的对象放入新的bucket数组中。这个过程叫作rehashing,因为它调用hash方法找到新的bucket位置。

9、重新调整HashMap大小存在什么问题吗?

  • 多线程环境下,如果多个线程同时检测到元素个数超过 负载因子*桶个数 这样会发生多个线程同时对hash数组进行扩容,都在重新计算元素位置以及复制数据。此时就存在了条件竞争,最终会形成一个循环链表,而导致死循环的产生。
  • 在这里插入图片描述
    https://blog.csdn.net/weixin_34228617/article/details/86730946
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值