HashMap之数组下标计算

20 篇文章 0 订阅

前提

HashMap是有数组+链表组成的,其中使用的算法有:hash(java8又使用了红黑树)

loadFactor

  • loadFactor是参与计算HashMap扩容的一个加载因子
  • new HashMap()会默认给loadFactor加载一个值0.75

capacity

  • capacity并非HashMap的属性,指的是HashMap数组的大小,即table.length

threshold

  • threshold是判断HashMap是否扩容的阈值
  • 官方注解上写的是 threshold = capacity * loadFactor

put时,数组下标计算

此处特别感谢zhangzhikai1同学的提醒

hash函数

static final int hash(Object key) {
    int h;
    return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}

putVal函数

if ((p = tab[i = (n - 1) & hash]) == null)
	tab[i] = newNode(hash, key, value, null);
  • 汇总为:table下标i=(table.length- 1) & ((h = key.hashCode()) ^ (h >>> 16))
  • 当我们put的时候,会根据key获取对应的hash值,然后无符号右移16位(>>> 16),在与原本的hash值进行^计算,然后再与table.length-1进行&计算,最终得出需要放入的位置
  • 比如:key = “165156”,HashMap中table数组的大小为16,套入公式,那么我们就会计算出存放位置的下标为13
  • 通过这种方式计算出的值只能是0–15,不行你自己试试
  • 我们来看一下为什么
    在这里插入图片描述

核心计算

  • (16-1)的二级制:0000 0000 0000 1111
  • &的计算方式是:同为1时为1,否则为0
  • 0000 0000 0000 1111的前12位都为0,那么和任何一个值进行&计算,最终得到的结果前12位只能为0
  • 0000 0000 0000 1111的后4位都是1,那么和任何一个值进行&计算,最终得到的结果还是原来的值
  • 最终的结果就是,把原有值的前12位归0,只保留后4位,最终转成十进制,结果集只能是0-15之间
  • 当前HashMap的table数组长度为16,下标值为0-15,膜拜我Doug Lea大神的神操作

扩容时,下标的重置计算

  • 在resize函数中,原HashMap的table数组扩容一倍,那么数组,以及链表中的对象,都需要重新分配位置
  • 那么在重新计算数组及链表中的位置时,情况分为三种
  • 第一种情况:数组有值,链表无值,这个时候只需要重新计算位置,放进去就OK了(e.hash & (newCap - 1))这个计算方式,就是上文数组下标计算,这里有个骚操作,可以结合第三种情况一起说
  • 第二种情况:数组有值,链表无值 ,但是红黑树有值,
  • 第三种情况:数组有值,链表有值,业务处理逻辑:循环列表判断每一个元素是否需要换位置,不需要的重新组合成链表,放入当前数组下标中,如果需要的,组成一个链表,放入指定的数组下标中
  • 第三种情况中判断元素是否需要换位置的依据是什么?if ((e.hash & oldCap) == 0) ,是这个判断,这个判断中e.hash指的是当前元素的hash值,oldCap指的是当前table数组的大小
  • 比如我们存放16个元素,而table数组的长度为4(原本默认为16,太大了,举例费劲),在假设16个元素的hash分别为0,1,2,3…一直到15,那么我们存放到table数组中的位置如下
下标0123
数组中的元素e0(hash值为0)e1(hash值为1)e2(hash值为2)e3(hash值为3)
链表中的元素e4(hash值为4)e5(hash值为5)e6 (hash值为6)e7 (hash值为70)
链表中的元素e8(hash值为8)e9(hash值为9)e10(hash值为10)e11 (hash值为11)
链表中的元素e12 (hash值为12)e13(hash值为13)e14(hash值为14)e15 (hash值为15)
  • 开始循环数组,获取到0下标,循环0下标中的元素(e0,e4,e8,e12)
  • e0,套入公式if((e.hash & oldCap) == 0) ------------ if((0&4)==0) ---------- true
  • e4,套入公式if((e.hash & oldCap) == 0) ------------ if((4&4)==0) ---------- false
  • e8,套入公式if((e.hash & oldCap) == 0) ------------ if((8&4)==0) ---------- true
  • e12,套入公式if((e.hash & oldCap) == 0) ------------ if((12&4)==0) ---------- false
  • e4,e12,是有需要移动的,那么移动的位置,我们也可以计算出来,是扩容后的下标为4的位置
  • 这既是Doug Lea的有一个神操作了,竟然都是需要移动到下标为4的地方,喊一波666。位置计算方式:数组下标计算
  • 而且基本上每个下标需要移动的数据n/2,取整,移动的位置刚好是当前位置i+扩容的差,如当前就是:i+4
  • 4
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值