HashMap集合新人理解篇!

49 阅读4分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第1天,点击查看活动详情

1.HashMap集合简介

HashMap基于哈希表的Map接口实现,是以key---value的方式存储实现,HashMap的实现是不同步的,意味着线程不安全的。他的key、value都可以为null,但是只允许一个key为null。

JDK1.8之前:

  • 数组+链表实现,数组时HashMap的主体,链表则是为了解决哈希冲突,

JDK1.8之后:

  • 数组+链表+红黑树实现。

为什么会出现链表

链表主要是为了解决哈希冲突,当两个对象调用的HashCode方法计算null哈希值,经哈希函数计算出来的地址被其他元素占用,存在的“拉链法”解决冲突。

何时转化为红黑树

当链表长度大于8,且数组长度大于64时,此时在次索引位置上所有数据改为红黑树存储。如果链表长度大于8,但是数组长度小于64时,此时数组只会扩容。

目的:因为数组比较小,尽量避开红黑树结构,这种情况下变为红黑树结构,因为红黑树要进行左旋、右旋、变色这些操作来保持平衡。,反而为降低效率,转化为红黑树查询效率为:O(logn),而不转化为红黑树,链表的查询速度为O(N),同时数组长度小于64,搜索时间要快一些,且红黑树一个节点的大小占用内存空间时红黑树节点大小的两倍,综上所述,为了减少搜索时间和提高性能,只有满足数组长度大于64链表长度大于8,此时索引位置上的链表才会转化为红黑树。

下面根据代码来讲解

HashMap<String, Integer> map = new HashMap<>();
map.put("李白", 87);
map.put("白居易",54);
map.put("王勃", 25);
map.put("李白", 47);
map.put("李清照", 24);
map.put("陆游"22);

image-20221111173755953.png

  • 第一步:当创建HashMap集合对象的时候,HashMap的构造方法并没有创建数组,而是在第一次创建一个长度为16的数组 Node[] table(JDK1.8之前用的是 Entry[] table) 用来存储键值对数据。

  • 第二步:当往map集合中添加<"李白",87>时,会根据“李白”所在的String类中重写后的hashCode()方法计算出值,然后结合数组长度采用某种算法将计算出数据在node数组中的索引值。如果计算出的索引空间无数据,则直接将<"李白",87>存到数组中。

  • 第三步:当往map集合中添加<"陆游",22>时,计算出的hashCode()方法结合数组长度计算出的索引也为1,此时1这个索引位置的数组空间不为null,此时会比较两个数据的hash值是否相等,如果不一致,则会在空间上划出一个节点来存储键值对<"陆游",22>,这种方式称为:拉链发。

  • 第四步:假设向哈希表中存储数据 <“李白”,47>,那么首先根据柳岩调用 hashCode() 方法结合数组长度计算出索引肯定是 0,此时比较后存储的数据“李白”和已经存在的数据的 hash 值是否相等,如果 hash 值相等,此时发生哈希碰撞。那么底层会调用“李白”所属类 String 中的 equals() 方法比较两个内容是否相等:

    相等:将后添加的数据的 value 覆盖之前的 value。

    不相等:继续向下和其他的数据的 key 进行比较,如果都不相等,则划出一个结点存储数据,如果结点长度即链表长度大于阈值 8 并且数组长度大于 64 则将链表变为红黑树。

哈希表底层采用何种算法计算hash值,还有那些算法可以计算hash值

答:底层调用key的hashCode()方法再结合数组长度进行无符号右移(>>>)、按位异或、按位与(&)计算出索引,还可以采用:平方取中法、取余法、伪随机数法。其中位运算效率高。

2.HashMap成员变量

1.static final int DEFAULT_INITIAL_CAPACITY = 1 << 4;

答:DEFAULT_INITIAL_CAPACITY 默认的出初始容量:右移四位=2^4=16,默认初始容量为16;

2.static final int MAXIMUM_CAPACITY = 1 << 30;

答: MAXIMUM_CAPACITY = 1 << 30 最大容量。2^30=1 073 741 824,HaspMap的最大容量为1 073 741 824

3.static final float DEFAULT_LOAD_FACTOR = 0.75f;

答: DEFAULT_LOAD_FACTOR = 0.75f默认负载因子为0.75,当向HashMap添加元素时,如果容量为16,需要添加第17个元素,就会扩容容量=16*0.75=12

4.static final int TREEIFY_THRESHOLD = 8;

答: TREEIFY_THRESHOLD = 8转化为红黑树的阈值,链表长度大于8,数组长度大于64,转化为红黑树

5.static final int MIN_TREEIFY_CAPACITY = 64;

MIN_TREEIFY_CAPACITY = 64最小树化容量,数组长度大于64,链表长度大于8,转化为红黑树