1.HashMap底层使用数组和单链表(Entitiy key value next)实现的。数组的每个值都是链表。
2.put 一个元素进去,对key进行一次hash计算,即是 hash(key)%len,(元素的key的哈希值对数组长度取模得到)作为数组下标index。 如果该index下面没有值,则放进去Entity[0]=A。如果该数组下标下已经有一个Entity实体了,则Entity[0] = B,B.next = A.如此类推,新入的值,放在链表头上。解决hash冲突(hash值计算后,对长度取模到供一个下标里面)问题。 解决hash冲突,链式方法。
3.get .在get方法中,首先根据key计算hash值,然后调用indexFor()方法(就是h & (length-1);)得到该key在table中的存储位置,得到该位置的单链表,遍历列表找到key和指定key内容相等的Entry,返回entry.value值。
get(key)方法时获取key的hash值,计算hash&(n-1)得到在链表数组中的位置first=tab[hash&(n-1)],先判断first的key是否与参数key相等,不等就遍历后面的链表找到相同的key值返回对应的Value值即可
4.自动扩容机制,超过负载因子0.75的之后,会扩容一倍。
5.关于jdk1.8所做的优化:
在JDK1.6,JDK1.7中,HashMap采用位桶+链表实现,即使用链表处理冲突,同一hash值的链表都存储在一个链表里。但是当位于一个桶中的元素较多,即hash值相等的元素较多时,通过key值依次查找的效率较低。而JDK1.8中,HashMap采用位桶+链表+红黑树实现,当链表长度超过阈值(8)时,将链表转换为红黑树,这样大大减少了查找时间。
加载因子(默认0.75):为什么需要使用加载因子,为什么需要扩容呢?因为如果填充比很大,说明利用的空间很多,如果一直不进行扩容的话,链表就会越来越长,这样查找的效率很低,因为链表的长度很大(当然最新版本使用了红黑树后会改进很多),扩容之后,将原来链表数组的每一个链表分成奇偶两个子链表分别挂在新链表数组的散列位置,这样就减少了每个链表的长度,增加查找效率。
在链表长度小于8的时候,新加入的同hash值的 元素是放在链表结尾的。因为放在链表头部会在并发的情况下造成性能缓慢。