JAVA日常学习--HashMap

本文详细解析了HashMap的工作原理,包括其底层实现、构造函数、resize方法、put方法以及线程安全性。探讨了HashMap如何通过数组和链表实现键值对存储,扩容机制,以及JDK1.8引入的红黑树优化。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.HashMap底层是一个数组,数组的每一项是一个链表,新建HashMap的时候会创建一个数组

transient Entry[] table;

static class Entry<K,V> implements Map.Entry<K,V> {
    final K key;
    V value;
    Entry<K,V> next;
    final int hash;
    ……
}

数组每一项都是Entry类型,是一个key与value的映射,并有指向下一个元素的指针。

2.HashMap构造函数

  • HashMap()
  • HashMap(int initialCapacity),如给定的容量不是2的幂次方,则使用比给定值大,并且最接近给定值的2的幂次方代替。
  • HashMap(int initialCapacity, float loadFactor)
  • HashMap(Map<?extends k,?extends v>m)

3.Resize()方法

扩容场景:在put()操作后,计算当前容器中的元素个数是否达到阈值,即容器容量负载因子,如果达到,那么就进行扩容操作,一般扩容为现在容量的一倍。

操作:首先计算扩容之后的容量,然后新建一个容器,将旧容器中的元素放入其中。元素扩容之后的hash值是原hash值增加一位,如原容器大小为16,则取key hash值的后四位,扩容后容器容量变为32,则取key hash值的后五位,即元素要么在原来的位置,要么在原来的位置在移动扩容长度的位置。而JDK7的hashmap扩容只会把原链表倒置放到现链表的位置。

4.put()方法

put方法如下图所示:

  1. 查看table是否为空,如果为空则扩容,如果不为空则执行第二步。
  2. 根据键值key计算hash值,得到要插入的位置i,如果为空,则直接插入,执行第六步,如不为空,则执行第三步。
  3. 判断当前插入值和第i个位置的首元素的值是否相等,如果相等则覆盖,否则执行第四步,这里相等位hashcode和equals
  4. 判断第i个位置是否为树节点(红黑树),如果是,则直接在树中插入节点,否则执行第五步。
  5. 遍历链表,如果链表长度大于8,则将链表转换位红黑树,并在树中插入节点,否则在链表中插入节点,如在遍历过程中发现key存在,则直接覆盖。
  6. 插入成功后,判断实际存在的键值数是否超过了threshold,如果超过了就执行扩容。

5.HashMap线程安全性

HashMap线程不安全,而ConcurrentHashMap线程安全

总结:

(1) 扩容是一个特别耗性能的操作,所以当程序员在使用HashMap的时候,估算map的大小,初始化的时候给一个大致的数值,避免map进行频繁的扩容。

(2) 负载因子是可以修改的,也可以大于1,但是建议不要轻易修改,除非情况非常特殊。

(3) HashMap是线程不安全的,不要在并发的环境中同时操作HashMap,建议使用ConcurrentHashMap。

(4) JDK1.8引入红黑树大程度优化了HashMap的性能。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值