1、HashMap是基于hash原理,通过PUT或GET来存储和读取Key、Value键值对的,当需要PUT一个键值对时,通过计算KEY的hashcode找到该键值对所在的bucket中的位置同时基于哈希碰撞的时候将键值对存储在bucket中的链表或者红黑树中。
2、在put元素的时候,如果产生hash冲突有以下几种情况:
(1)、如果数据中存储的是红黑树,那么将该键值对插入红黑树中。
(3)、如果数组中存储的是链表,则将数据插入链表中如果该链表中的键值对大于等于8并且hashmap中的键值对小于64则扩容,如果大于等于64则转化为红黑树。
3、如果hashmap中的键值对数量大于等于临界值的数量则扩容。这个JDK1.8和JDK1.7中有区别的 在JDK1.7中还要判断如果产生哈希冲突之后才会扩容,这样有可能会导致hashmap中的键值对数远大于临界值。
4、JDK1.7中多线程往hashmap中put元素会导致2个问题:
(1)、如果新增Entry的时候两个线程指向后面的元素都是e,那么会导致数据丢失。
(2)、由于JDK1.7中PUT元素扩容的时候采用的是头插法,所以如果多线程PUT数据有可能会导致死循环,如果持续这样会导致CPU飙升到100%,JDK1.8就采用了尾插法。
5、JDK1.8中,取hash值优化成了将key的hashcode高16位和低16位做位运算(异或)运算,这样首先将所有位都加入了运算减少了冲突,其次位运算效率高。
6、JDK1.7扩容需要对所有节点重新进行rehash计算然后将Entry插入新的数据中,但是JDK1.8通过用hash值与扩容后的(n-1)做逻辑与运算,将bucket中的结点分成两部分需要迁移的和不需要迁移的的节点然后就可以直接插入到新数组的对于位置了。
原文:https://www.cnblogs.com/rhodesis/p/12863480.html