集合(三)HashMap源码分析

集合(一)扒掉ArrayList的外衣

集合(二)走进LinkedList的内心

集合(三)HashMap源码分析

前两篇学习了ArrayList和LinkedList的源码,一个是数组一个是双向链表,今天一起来看看HashMap(Jdk1.8)。

HashMap的数据结构是什么样呢

我们直接先看其put方法来看它是把数据放在什么结构内的。

 这里我们看有一个hash(key),这里对key做hash计算:把key的hashcode()值与该值向右移动16位(空位补0)按位异或,hash计算决定了数据在HashMap数组结构中的存储位置。

putVal方法:

上图1的位置, Node<K,V>[]tab,Node?是不是有点熟悉,LinkedList的时候我们看到过,所以说HashMap的数据存储结构包含了数组和链表,是二者的组合结构。

和LinkedList的Node不同,HashMap的Node只有next指向它后一个元素,说明在HashMap数据结构内部采用的是单向链表。

那么问题来了,如果这个链表very long,那查询起来岂不是很伤 

Jdk1.8大婶们已经优化了这个问题,看putval方法3的位置treeifyBin(),当链表长度大于8-1时,链表由线性树化,这是Java8中HashMap优化的地方通过红黑树二分查找加快查询速度。(红黑树推荐大神博客漫画:什么是红黑树?) 

 HashMap为何采用链表呢,数组不香嘛

 HashMap中每一对key,value具体放在那个位置,这个索引是根据key的hash算出来的,数据千千万,后来的key计算出来的hash结果的存储地址有可能已经存储了数据,这就尴尬了,有矛盾就要解决啊。

解决哈希冲突的方法有:开发定址法(继续找下一块存储地址)、再散列函数法(采用另外的散列函数再次计算)、链地址法(数组+链表),HashMap就采用了链地址法。冲突的数据就绑一串吧。

数组?数组长度呢?扩容呢?

 HashMap有四个构造函数,默认的容器长度是1<<4(16),当然和ArrayList一样我们可以传入容量的初始大小。

在无参构造初始化一个系统默认DEFAULT_LOAD_FACTOR=0.75f是什么鬼,负载因子,当长度达到最大容量的0.75时就需要扩容了,也就是说HashMap并不是满了才扩容。

HashMap扩容:容量扩大为原来的<<1(2)倍,然后创建一个新的Node数组,Jdk1.7是把老的数组遍历重新hash计算到新数组,Jdk1.8是判断原来的hash值新增的那个bit是1还是0,是0的话索引没变,是1的话索引变成“原索引+oldCap”。 

HashMap使我们比较常用的数据结构,在多线程操作时它也是线程不安全的,需要线程安全使用HashTable和ConcurrentHashMap。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值