HashMap深透源码刨析

本文深入探讨了位操作,包括位与、位或和位异或的逻辑,并举例说明。接着,重点讲解了哈希表的哈希值计算,强调了哈希函数设计的重要性,尤其是为何哈希表的长度要求为2的幂次方。还分析了哈希冲突的处理策略,包括扩容和数据结构的选择,如链表和红黑树,并讨论了多线程环境下的问题及解决方案。
摘要由CSDN通过智能技术生成

位 与:都为1,才为1

10&5
00000000000000000000000000001010

& 00000000000000000000000000000101

00000000000000000000000000000000

位 或:只要有个一个为1,就为1

10|5
00000000000000000000000000001010

| 00000000000000000000000000000101

00000000000000000000000000001111  === 15

位 异或:两个数不一样,才为1

10^5
00000000000000000000000000001010

^ 00000000000000000000000000000101

00000000000000000000000000001111  === 15

哈希值

通过一定的散列算法,把一个不固定长度的输入,转成一个固定长度的输出,输出的结果我们称之为哈希
map中,hash就是一个int值

哈希表:存储哈希值的数组 – 存取散列值(哈希值)的一个容器

哈希值到底该如何存,该如何取呢??? -- 通过数组的角标实现数据的存取
需要有一个映射:不同的hash值存在对应角标位
	hash值 ----运算---》 index

哈希函数:将哈希值通过某种运算规则得到对应index

如何让存取效率是最高的??? – 如果元素都是均匀存储在数据的角标位,而不产生冲突,就是最好的

① 尽可能少的产生hash冲突
	hash函数计算出来的角标要尽可以能均匀
		static int indexFor(int h, int length) {
			return h & (length-1); //length是map中数组的长度
		}
		
	length长度的规则是什么?? -- 要求是2的幂次方数,为啥???
		
		假设长度是8 -1  === 7
		
			00000000000000000000000000000111  --- 长度8,参与hash运算是7
			00000000000000000000000000001111  --- 长度16,参与hash运算是15
			00000000000000000000000000011111  --- 长度32,参与hash运算是31
			
			00110011000111111001110111101111
		&	00000000000000000000000000001111
		--------------------------------------------
			00000000000000000000000000000000 -- 00000000000000000000000000001111
			【得到的值,正好是0-15的范围,该范围正好是长度为16的数组的角标】
	假设长度不是2的幂次方:
		奇数  -- 长度5
				00101001001001011111111111100111
			&	00000000000000000000000000000100   -- 长度5,参与hash运算是4
			------------------------------------------
				得到的结果永远是一个偶数,代表获取的角标永远是偶数,奇数位的角标永远会都没有值
				【数组的就浪费掉了一半】
		
		偶数  -- 长度6
				00101001001001011111111111100111
			&	00000000000000000000000000000101   -- 长度6,参与hash运算是5
			-------------------------------------------------
				第二位永远是0,导致2,3角标位都不可能有值
				【数组的就浪费掉了一半】
				
	2的幂次方,在扩容时,扩容后的数组长度是原数组长度的2倍,结果还是2的幂次方
	有啥好处???
		假设原来是8
			00000000000000000000000000000111  
		扩容后是16
			00000000000000000000000000001111
		在扩容时,数据要从原数组中迁移到新数组中
			00000000000000000000000000001101
			00000000000000000000000000000111 
			-------------------------------------
										 101  === 5 原数组的角标位
										 
			00000000000000000000000000001101
			00000000000000000000000000001111
			--------------------------------------
										1101  ==== 13(原角标位+扩容长度) 新数组的角标位
		
			【扩容后,数据迁移时,数据要么在原来位置,要么在原来位置+扩容长度
			不需要重新hash--效率更好】
	
② 确实产生了hash冲突 -- 扩容+数据结构
	扩容
		什么时候扩容
			jdk1.7
				判断是否到达阈值(0.75*数组长度)
				同时是否产生hash冲突
				
				扩容后,再添加元素
			
			jdk1.8
				先添加元素
		
				判断是否达到阈值
		怎么扩容
			jdk1.7	
				添加元素使用头插法
				
				将单向链表的数据进行迁移
			jdk1.8
				添加元素使用尾插法
				
				如果对应角标位是单向链表,将单向链表进行数据迁移
				如果对应角标位是红黑树,将双向链表进行数据迁移
					如果发现迁移完数据后,双向链表的结构小于/等于6,会将红黑树重新转回单向链表的结构
		扩容后有什么问题(多线程环境)
			jdk1.7
				多线程环境下会形成环形链表
				
			jdk1.8
				多线程环境下会有数据丢失的问题
	
	数据结构解决
		jdk1.7产生冲突后,会形成单向链表
		
		jdk1.8产生冲突后
			会形成单向链表
				如果单向链表的长度大于/等于8,会转成红黑树+双向链表(扩容时使用)

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值