1、存储结构
static class Node<K,V> implements Map.Entry<K,V> {
final int hash;
final K key;
V value;
Node<K,V> next;
Node(int hash, K key, V value, Node<K,V> next) {
this.hash = hash;
this.key = key;
this.value = value;
this.next = next;
}
.....
}
底层数据结构是:数组+链表;在jdk1.8之后,当链表节点长度大于8时;会将链表转化为红黑树;如果节点小于等于6时候,又会将红黑树转化为链表。
对key而言,如果为null,则返回0;反之得到其hashCode,然后将hashCode进行右移,再进行异或算法(即: hashCode高16位和低16位进行异或),低16位保证足够散列,保证hash值足够均衡。(返回的结果的低16位既有高16位的数字特征又有低16位的数字特征、高16位保持不变)
0000 0000 1000 0000 1001 1100 0101 1101
^ 0000 0000 0000 0000 0000 0000 1000 0000
0000 0000 1000 0000 1001 1100 1101 1101
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
hashCode()函数是调用native方法
public native int hashCode();
3、取模运算
一个数字对2的n次方进行取模,定义m是2的n(n>=1)次方,那么k对m进行模运算,k%m = k&(m-1)
示例如下:
- 10%4 = 2;
- 10&(4-1) = 3
HashMap底层是数组,在计算将key放到哪一个index下,直接将hashkey&(length-1)进行与运算(length是2的n次方)即可。
4、关于扩容问题
- loadFactor 是装载因子,主要目的是用来确认table 数组是否需要动态扩展,默认值是0.75,比如table 数组大小为 16,装载因子为 0.75 时,threshold 就是12,当 table 的实际大小超过 12 时,table就需要动态扩容;
- table 数组大小是由 capacity 这个参数确定的,默认是16,也可以构造时传入,最大限制是1<<30;