HashMap

加载因子:loadFactor

  • 默认0.75,为扩容使用;
  • 初始化 或 扩容方法resize(),初始化数组长度默认16(2的幂次方);

阈值:threshold:

        整个hashMap的元素个数(size属性)超过此值就会扩容;

        每次都是双倍扩容;

        扩容第二个地方:其中一个链表长度>=8时,会去优化单向链表,数组长度小于64,扩容,大于等于,当前下标的所有node节点转换为双向链表+红黑树;

修改次数modCount

数据对象数组:table,Node

hashCode是int类型,一个int是4个字节,一个字节8bit位,初始化的长度16,计算下标是15,所以低4位一直就是1,高28bit位是0

Map<String, Object> map = new HashMap<>(len);

初始化:不传默认16;传入的len如果不是2的幂次方,那么会处理成大于len的2的幂次方数值 tableSizeFor(len)

生成的map对象是放在jvm的堆中,图中圈出来的属于线程栈的对象,因为方法内需要多次使用到,所以如果每次都从堆中获取table会有性能上的消耗,直接从线程栈中取值会更快

计算K的hash值:(key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);

  h右移16位,让hashCode的高位也参与到下标的计算

 由K的hash值和数组长度计算Node节点的下标,

if ((p = tab[i = (n - 1) & hash]) == null)  // 该下标元素为null,那么把新的node放到该下标
    tab[i] = newNode(hash, key, value, null);

如果不为空,就拼接到该下标的链表尾部(jdk1.7是头插法),如果该链表长度 >= 8,那么就会对map的数据进行处理,避免单向链表检索数据性能慢;O(n);这里实际判断的时候链表长度是9,因为p也是一个节点,然后又拼接了一个next,所以长度是9

在转红黑树之前会判断数组的长度是否大于64,如果小于64,那么会对数组进行扩容,均摊到其他下标中,保证链表长度小于8;如果链表长度大于等于8,并且map的数组长度大于64了,那么就把当前下标的Node单向链表转换成 双向链表+红黑树 结构 TreeNode,复杂度O(log n)。

 

扩容:

扩容时是双倍扩容,也就是原容量左移一位,

对单链表拆分:会判断高位或者低位,高位链表下标=原下标+原数组大小;

 对TreeNode拆分:如果拆分也是区分高位或者低位,拆分的链表长度 <= 6,那么就会又变为单向链表;剩余Node需要重新生成一个树结构(低位判断是否有高位链表,有,说明原来的树结构有拆分,反之亦然)

有了数组,为什么还要使用hashmap?

首先,hashmap是数组+链表的结构,数组可以快速定位,链表可以提高写入速度,结合了数组和链表的优点。

java7:使用头插法:速度是最快的

java8:尾插法,红黑树

总结:

链表用头插法是最快的

数组:查询快,插入慢

         定位速度快,可以快速插入,插入之后需要后移

链表:查询慢,插入快,定位速度慢,插入后不需要移动

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值