HashMap内部原理深入理解

HashMap原理深入理解

hashing(哈希法)的概念
散列法(Hashing)是一种将字符组成的字符串转换为固定长度(一般是更短长度)的数值或索引值的方法,称为散列法,也叫哈希法。由于通过更短的哈希值比用原始值进行数据库搜索更快,这种方法一般用来在数据库中建立索引并进行搜索,同时还用在各种解密算法中。

HashMap概念和底层结构

HashMap是基于哈希表的Map接口的非同步实现。此实现提供所有可选的映射操作,并允许使用null值和null键。HashMap储存的是键值对,HashMap很快。此类不保证映射的顺序,特别是它不保证该顺序恒久不变。

HashMap 内部结构:可以看作是数组和链表结合组成的复合结构,数组被分为一个个桶,每个桶存储有一个或多个对象(键值对),每个对象(键值对)包含三部分key(键)、value(值),next(指向下一个对象),通过哈希值决定了对象(键值对)在这个数组的寻址;哈希值相同的对象(键值对),则以链表形式存储。如果链表大小超过树形转换的阈值(TREEIFY_THRESHOLD= 8),链表就会被改造为树形结构(红黑树)。

HashMap的内部结构示意图如下:
在这里插入图片描述
查询时间复杂度:HashMap的本质可以认为是一个数组,数组的每个索引被称为桶,每个桶里放着一个单链表,一个节点连着一个节点。很明显通过下标来检索数组元素时间复杂度为快,而且遍历链表的时间复杂度是慢。

数组:存储区间连续,占用内存严重,寻找容易,插入删除困难;
链表:存储区间离散,占用内存比较宽松,寻找困难,插入删除容易;
Hashmap综合应用了这两种数据结构,实现了寻找容易,插入删除也容易。

HashMap如何重新调整容量的大小

HashMap的扩容阈值(threshold(扩容阈值) = capacity(容量) * loadFactor(负载因子) ),就是通过它和size进行比较来判断是否需要扩容。默认的负载因子大小为0.75,也就是说,当一个map填满了75%的桶时候,和其它集合类(如ArrayList等)一样,将会创建原来HashMap大小的(capacity(容量) * loadFactor(负载因子)) 的桶数组,来重新调整map的大小,并将原来的对象放入新的桶数组中。这个过程叫作rehashing(重散列),因为它调用hash方法找到新的桶位置。

解决 hash 冲突的常见方法

针对哈希表直接定址可能存在hash冲突,举一个简单的例子,例如:
第一个键值对A进来,通过计算其key的hash得到的index=0。记做:桶[0] = A。
第二个键值对B,通过计算其index也等于0, HashMap会将B.next =A,桶[0] =B,
第三个键值对C,通过计算其index也等于0,那么C.next = B,桶[0] = C;
这样我们发现index=0的地方事实上存取了A,B,C三个键值对,它们通过next这个属性链接在一起。 对于不同的元素,可能计算出了相同的函数值,这样就产生了hash 冲突,那要解决冲突,又有哪些方法呢?具体如下:

a. 链地址法:将哈希表的每个单元作为链表的头结点,所有哈希地址为 i 的元素构成一个同义词链表。即发生冲突时就把该关键字链在以该单元为头结点的链表的尾部。

b. 开放定址法:即发生冲突时,去寻找下一个空的哈希地址。只要哈希表足够大,总能找到空的哈希地址。

c. 再哈希法:即发生冲突时,由其他的函数再计算一次哈希值。

d. 建立公共溢出区:将哈希表分为基本表和溢出表,发生冲突时,将冲突的元素放入溢出表。

HashMap采用哪种方法解决冲突的呢?

HashMap 就是使用链地址法来解决冲突的(jdk8中采用平衡树来替代链表存储冲突的元素,但hash() 方法原理相同)。当两个对象的hashcode相同时,它们的桶位置相同,碰撞就会发生。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值