HashMap的底层数据结构
数组+链表或红黑树。
JDK8 中对哈希算法和寻址算法是如何优化的
哈希算法的优化:原哈希值跟右移 16 位以后的值做了一个 ^ (异或)运算。
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
这样的优化可以尽量避免一些哈希值后续出现冲突。
寻址算法的优化:没有用哈希值对数组长度取模,而是让哈希值与数组长度减 1 进行了&运算
(n - 1) & hash
同样的效果,&运算比取模运算性能高很多。这是一个数学问题,这里不予探究。
HashMap是如何解决hash碰撞问题
多个不同的key经过hash算法与寻址算法定位出的位置是一样的,这个情况就叫做哈希碰撞,或者叫做哈希冲突。
HashMap会在这个位置挂一个链表,然后在这个链表里面,放入多个元素。这样的话就可以解决哈希冲突的问题,但是如果这个链表很长的话,可能会导致后续找它的时候,遍历链表,这个性能会比较差一些,这个时候HashMap又做了一个优化,这个优化就是链表达到一定长度之后,会把链表转换为红黑树。它的好处在于遍历一棵红黑树这个时间复杂度性能会比链表高一些。
HashMap是如何进行扩容的
超过阈值(数组长度*负载因子)就会扩容,默认来说是两倍扩容。它扩容之后,会重新去进行&运算。