面试总结:HashMap

介绍

  • 实现了Map接口,存储的是键值对形式数据
  • jdk1.8之前底层数据结构是数组+链表,jdk1.8之后是数组+链表+红黑树
  • 非线程安全的
  • key和value都可以为null值,key为null的数据存在数组下标为0 的位置

底层原理

底层数据结构

jdk1.8之前底层数据结构是数组+链表,jdk1.8之后是数组+链表+红黑树

重要属性

  1. 初始化容量:16
  2. 负载因子:0.75
  3. 阈值:容量*负载因子
  4. 扩容时机:当HashMap中的元素数量超过了负载因子(load factor)与当前容量的乘积时,就会触发扩容操作
  5. 树化时机:当HashMap的容量超过阈值(默认为64)且某个桶(bucket)中的元素个数超过8时,会触发树化操作。这是为了在高负载情况下提高查找效率,将链表结构转换为红黑树结构
  6. 反树化时机:当红黑树结构中的元素个数小于等于6时,会触发反树化操作。这是为了在负载较低的情况下,避免维护红黑树的开销,将红黑树结构转换回链表结构
  7. 扰动函数:

1.先用key的hashCode()值与它的高16位进行异或得到hash值

2、hash值再和初始容量-1进行与预算(h&(n-1))。

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

新增元素过程

在JDK 1.7中,HashMap的新增元素过程如下:

1. 首先,根据键对象的hashCode()方法计算哈希码。

2. 使用哈希码和HashMap的容量计算数组索引,确定元素在内部数组中的存储位置。

3. 如果该位置上已经存在元素,则通过equals()方法比较键对象是否相等。如果键对象相等,则更新对应的值,否则发生了哈希冲突。

4. 如果发生哈希冲突,使用链表来解决冲突。新的键值对将被插入到链表的头部

在JDK 1.8中,HashMap的新增元素过程进行了一些优化,引入了红黑树以及尾插法来提高性能:

1. 首先,根据键对象的hashCode()方法计算哈希码。

2. 使用哈希码和HashMap的容量计算数组索引,确定元素在内部数组中的存储位置。

3. 如果该位置上已经存在元素,则通过equals()方法比较键对象是否相等。如果键对象相等,则更新对应的值,否则发生了哈希冲突。

4. 如果发生哈希冲突,HashMap会先判断该位置上的链表是否已经转换为红黑树,如果是红黑树,则按照红黑树的插入方式进行插入。

5. 如果该位置上的链表没有转换为红黑树,则将新的键值对插入到链表的尾部。

6. 在插入新的键值对后,如果链表的长度超过阈值(默认为8),并且当前HashMap的容量超过了64,链表将被转换为红黑树。

红黑树在查找和插入操作上具有更好的时间复杂度,尾插法可以减少链表遍历的次数,提高插入的效率。

需要注意的是,JDK 1.8中的HashMap在处理哈希冲突时,会先尝试使用尾插法插入新的键值对,只有在链表长度超过阈值并且HashMap的容量超过一定大小时,才会将链表转换为红黑树。这样可以在大部分情况下避免不必要的红黑树转换,提高了性能和效率。

扩容过程

1.将数组容量扩大为原来的两倍

2、遍历原来数组中每个元素,重新计算下标后调用put方法

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值