java集合学习之Map(一)HashMap

首先,本人鄙视了...........下面开始自己的知识整理

--------------------------------------------------------------------------------------------------------------------

先确定下几个概念

1.HashMap采用的是数组加链表的形式存储数据

2.HashMap中有DEFAULT_INITIAL_CAPACITY(初始化大小,默认为16)DEFAULT_LOAD_FACTOR(加载因子,默认为0.75f)

3.什么是hashCode(哈希码),个人理解就是,通过某种算法,尽量让同一个类的对象按照自己不同的特征尽量的有不同的哈希码。

4.与运算,1&1=1,其余为0

---------------------------------------------------------------------------------------------------------------------

正文:

    从创建一个HashMap说起,一共有种方法创建一个对象:

java集合学习之Map(一)HashMap

但最终都会调用第3个方法,直接从第三个方法开始整理:rt

java集合学习之Map(一)HashMap

首先看参数:initialCapacity就是初始化大小,loadFactor就是加载因子,如果调用无参的构造方法,这两个参数的值就是概念的2,看下参数MAXIMUM_CAPACITY = 1<<30;就是2的31次方,这是HashMap允许的最大长度。

看下while循环,(容量<初始容量){容量就变为原来的2倍,就是2的次幂倍数},大体看一下构造,从put方法说起,感觉put方法比较容易讲解:

java集合学习之Map(一)HashMap

首先,如果key==null的话,就调用null值方法,put进去。重点在后面,key!=null,计算出key的hashcode值hash,在将hash与table.length-1做与运算java集合学习之Map(一)HashMap为什么这么做?

在构造函数中可以看到table的length就等于capacity,在while循环中也可以看到,capacity为2的次幂,indexFor方法里做的是两者的位运算,即直接通过2进制来运算。整理下现有的数据:capacity:转换成2进制肯定为1000.....000(就是length),然后length-1=0111..111,我们假定capacity=16转成2进制就是10000,length-1=01111,所以说无论h为什么值,都是只有h的后四位和length-1的后四位参与运算,(length-1出了后四位都是0,最后与运算结果还是0)。所以结果只在0-1111之间,保证了最后产生的【i】【该key所对应的entry在数组中的位置】,只会在table(length=16,0-15)的范围内。

所以会有一个问题产生,就是所有的key,最后计算出来的位置肯定都在16个之一。那么不同的key,不同的hashCode计算出来的【i】可能会一样【【hash碰撞的问题,hashMap不可避免的问题】】,这就是为什么需要用链表了,根据hash值去找链表里遍历,找到key。Entry结构如下

java集合学习之Map(一)HashMap

【h&(length-1)也相当于 h%length,随便举个例子就能得出,为什么用位运算,肯定是速度快了】

为什么length是2的次幂,不是随便一个数,我觉得啊,真的是我觉得,可能是这样运算方便吧......

【举个例子:如何length=298,h=315,转换成2进制,就要每个位数进行计算,当length很大,h也很大时肯定是一种累赘了,虽然位运算确实很快】

但这就涉及到了另一个问题【负载因子】了。等会再说,mark一下。【ps:负载因子是决定hashmap这个table何时扩充的主宰因素】

看代码,for循环,我们得到了key在数组中的位置【i】,和key的hashcode,就要去遍历看一下有没有这个key,上边说到不同的key可能会在同一个位置,所以我们需要根据key的hashcode去寻找我们要找的那个,如果有就覆盖掉,如果没有..就接着走呗,调用addEntry方法:

java集合学习之Map(一)HashMap

我们来解释下这几个参数:size就是map的大小就是有多少个链表吧,但并不是数组的容量。

【threshold,重点说下】在前面两个参数的构造函数中看到:threshold = (int)Math.min(capacity * loadFactor, MAXIMUM_CAPACITY + 1); 取 数组容量*负载因子的值去和2的31次方+1比较取小值,【我们要根据loadFactor去决定何时扩容!!!!】因为Map的最大就是2的32次方了。如果当前链表的个数大于负载的个数了,我们就需要扩充和复制了,搞一个新的数组出来,重新计算hash值和新的key的位置。看下resize

【负载因子】【默认加载因子 (.75) 是在时间和空间成本上寻求一种折衷。加载因子过高虽然减少了空间开销,但同时也增加了查询成本】这就是传说中的【"时-空"矛盾的平衡与折衷】

java集合学习之Map(一)HashMap

再看下createEntry

java集合学习之Map(一)HashMap

我们就取【i】的entry,然后给创建一个新的entry把要put的key,value放进去,把【i】的entry放到next,就相当于给链表加头结点吧。最后重新赋给table【i】。

感谢:

高爽|Coder

http://blog.****.net/ghsau/article/details/16890151  的文档,很有启发