Collection源码分析(三):HashMap源码分析

点来HashMap的源码 我们可以看到 HashMap继承至AbstractMap 并实现Map<K,V>, Cloneable, Serializable三个接口

可以看到主要的以下几个变量

可以看到DEFAULT_INITIAL_CAPACITY 是默认的初始化容量

MAXIMUM_CAPACITY是最大容量

DEFAULT_LOAD_FACTOR 是增长因子

EMPTY_TABLE这是一个Entry数组 

这个Entry有四个参数 一个key,一个Value (键值对)一个next变量 用于指向下一个元素一个整形的Hash值

可以看到hashMap的构造器 在你New一个hashMap的时候 无参构造器默认调用的是这个有两个参数的构造器传的 值 是上述 DEFAULT_INITIAL_CAPACITY 和 DEFAULT_LOAD_FACTOR值 主要操作就是将threshold和 loadFactor 装载因子 赋值 init()是一个空方法

threshold是HashMap的一个扩充标量 当HashMap的元素数量 达到这个值得时候 则HashMap的数组部分会进行扩充 下面会说

我们再来看一下put方法 是如何添加元素的

1.首先第一个判断 如果是刚new出来的hashmap会走第一个IF(这两个引用的是同一块内存地址) 方法如下

其中这个roundUpToPowerOf2方法 是取大于toSize 但是是2,4,8,16,32...2^n中最接近toSize的最小值  这就是hashMap自增长的方法

如下

将number-1 并左移一位 完成的效果就是 1010(10) - 0001 =1001 << 1 =10010 取最高位 其他补0 =10000=16

将整个本来为10 size的 table扩充为16长的一个Entry数组

2.如果key是一个null值 将其加入table中 如果不为null 则计算传入的对象的hash值

并用hash值的二进制与(表长-1)的二进制做与(&)操作 注意 此处表长必为2的倍数 

如:表长16  则表示范围为:00000-01111 (在计算机中前面补0 的原码)与元素的hash值 做与(&)操作 1&0=0,0&0=0 ,&1=1 

这个操作可以看作取表长的余数 得到存储的位置 i (散列点)

3.通过一个For循环 遍历存储在该节点的元素 Entry链 查找是否有相同的hash值得元素 并与之进行值替换  将旧值加在 第这个节点后面 并返回oldValue旧值

4.如果没有找到有相同hash值得元素则通过addEntry方法 将元素加入算出的位置

可以看到 如果你的key-value得数量大于整个table的 设定的扩充值 threshold 这个值在初始化得方法里可以看到 inflatetable

取得是默认长度*增长因子(0.75)也就是说 整个hashmap得散列覆盖率 达到整个hashmap数组得 75% 就需要进行扩充  将整个table 扩充为原来得2倍(将table拷贝到新的table数组中 并重新计算扩充值)

这个扩充值相比较 是hashMap所有元素的个数

并重新计算 hash值和散列点bucketIndex 添加元素到table数组上

可以看到 hashmap一个数组+链表实现的数据结构 通过hash值取表长的余数 定位要存的数组节点 并判断此处的链表是否有值 如果有 将新的值插入到链表的前端 并且通过默认的75%的覆盖率 进行自增长

可以考虑到极限情况 如果说 所有的元素 key值散列到 同一个数组元素下会有什么效率问题 如图

猜你喜欢

转载自my.oschina.net/u/2970507/blog/1799868