第一 HashMap特点:HashMap是基于Map接口的实现,储存键值对时,它可以接收null的键值,是非同步的,HashMap存储着Entry(hash, key, value, next)对象。 table中存储的是Entry的单向链表。
第二 HashMap工作原理:通过hash的方法,通过put和get存储和获取对象。存储对象时,我们将K/V传给put方法时,它调用hashCode计算hash从而得到bucket位置,进一步存储,HashMap会根据当前bucket的占用情况自动调整容量(超过Load Facotr则resize为原来的2倍)。获取对象时,我们将K传给get,它调用hashCode计算hash从而得到bucket位置,并进一步调用equals()方法确定键值对。如果发生碰撞的时候,Hashmap通过链表将产生碰撞冲突的元素组织起来。
put函数原理:大致的思路为: 1.对key的hashCode()做hash,然后再计算index; 2.如果没碰撞直接放到bucket里; 3.如果碰撞了,以链表的形式存在buckets后; 4.如果碰撞导致链表过长(大于等于TREEIFY_THRESHOLD),就把链表转换成红黑树; 如果节点已经存在就替换old value(保证key的唯一性) 5.如果bucket满了(超过load factor*current capacity),就要resize。
get函数:大致的思路为: 1.bucket里的第一个节点,直接命中; 2.如果有冲突,则通过key.equals(k)去查找对应的entry 若为树,则在树中通过key.equals(k)查找,O(logn); 若为链表,则在链表中通过key.equals(k)查找,O(n)
HashMap resize 原理:当超过限制的时候会resize,然而又因为我们使用的是2次幂的扩展(指长度扩为原来2倍),所以,元素的位置要么是在原位置,要么是在原位置再移动2次幂的位置。 如果 hashmap 的容量为 16 的话,那么对应的 key 的 hash 的后四位则为 bucket 的 index;如果 hashmap 的容量为 32 的话,那个对应的 key 的 hash 的后五位则为 bucket 的 index。
HashMap长度必须是2的幂次原因:
先了解 下标的计算方法:
index = HashCode(Key) & (Length - 1) 下面我们以值为“book”的Key来演示整个过程:
1.计算book的hashcode,结果为十进制的3029737,二进制的101110001110101110 1001。
2.假定HashMap长度是默认的16,计算Length-1的结果为十进制的15,二进制的1111。
3.把以上两个结果做与运算,101110001110101110 1001 & 1111 = 1001,十进制是9,所以 index=9。
可以说,Hash算法最终得到的index结果,完全取决于Key的Hashcode值的最后几位。
如果HashMap长度为10:
这样,显然不符合Hash算法均匀分布的原则。
反观长度16或者其他2的幂,Length-1的值是所有二进制位全为1,这种情况下,index的结果等同于HashCode后几位的值。只要输入的HashCode本身分布均匀,Hash算法的结果就是均匀的。