PS:由于文档是我在本地编写好之后再复制过来的,有些文本格式没能完整的体现,故提供下述图片,供大家阅览,以便有更好的阅读体验:
1.HashMap底层数据结构是数组+链表(jdk1.7头插法<扩容时链表逆序可能会导致环形链表的问题出现> jdk1.8尾插法)+红黑树(jdk1.8).
2.HashMap中数组的容量默认为16,负载因子默认为0.75,当数组的0-15个下标里有16×0.75=12个被使用时,且HashMap中存储的元素总个数大于64时,则发生扩容操作,数组的容量扩大为原来的2n.
3.负载因子代表数组中存储数据密度的大小:负载因子越大,数组单位容量内存储的数据越多,不同元素之间(key不同,但计算得到的数组下标相同)越容易发生碰撞(哈希碰撞);反之,则单位容量内存储的数据越少,越不易发生碰撞.
4.put(key,value)时的处理逻辑:
(1)hash(key) = (key==NULL)?0:(h=key.hashCode())^(h>>>16),即取key的原始哈希码的高低16位进行异或位运算,计算出一个经高低位混合后高低位分布更加均匀的新哈希码,再用该新哈希码和数组容量减一做与位运算(hash(key)&(2^n-1))得出要存放的数组下标[当HashMap的容量是2的n次幂时,(2^n-1)的二进制就是11111*111全1的形式,这样与hash(key)进行与的位运算时,能够充分的散列,使得添加的元素均匀分布到HashMap的每个位置上,减少hash碰撞],可以一定程度降低根据不同元素计算得出相同数组下标(哈希碰撞)的概率;
(2)得出的数组下标里已经存放有数据元素,则根据key的值遍历比较该下标下的链表或红黑树,如果遇到相同key,则更新key对应的value值后返回(return);若遍历链表后未遇到相同的key,则在链表(jdk1.7头部 jdk1.8尾部)或红黑树合适位置(可能触发红黑树的左旋右旋或颜色改变),插入新的(key,value)键值对元素(插入值时统计存储的总元素个数的变量值会加一) ;
(3)某链表插入新的键值对后,可能导致该链表的长度大于等于8,若此时数组存储的总元素个数大于等于64,则将链表转为红黑树(保证最好最坏情况下时间复杂度为以2为底总元素个数n的对数级别,以提高增删查处理性能);
(4)链表插入新值后,也可能会触发数组扩容操作
5.get(key)时的处理逻辑:同put(key,value)的步骤(1),会根据key值计算得出数组下标,然后用key值遍历比较该数组下标下的链表或或红黑树,找到相同key对应的value值并返回,否则,返回null;
6.remove(key)时的处理逻辑:同get(key)和put(key,value)的步骤(1),会先根据key找到要删除元素所在位置,然后进行删除操作;