HashMap:
数组+链表+红黑树(jdk8 链表数大于8,转为红黑树存储)
数组权当hash桶,链表或红黑树为桶里的数据,通过对key进行hash及取模获取到数组下标,然后在数组下标获取对应的链表或红黑树,进行插入、查询、删除操作。
上边的通过对key进行hash及取模获取到数组下标分析:
jdk7
key.hash&(capacity-1) (说明:此处是二进制与计算,其实就是对key的哈希值进行取模。等同于10进制计算 key.hash % capacity的效果,但是效率比这高。其实都是为了获取0到capacity-1的一个值。)
源码:
static int indexFor(int h, int length) {
return h & (length-1);
}
注:capacity为容量,默认容量为16,扩容时2倍扩容。及capacity始终为2的幂次方。因为2的幂次方减一 二进制都为 1111...111(位数为x),进行与计算时,结果就为key的hash值后x位数的值。所以只要保证hash是均匀分布的,就可以保证取的数组下标是均匀分布的。可以尽量避免hash冲撞问题(即不同key所取数组下标相同)。
jdk8
因为jdk8加入红黑树逻辑,所以hash冲撞时性能影响不大。因此key.hash时,只取了前16位异或下来的数据(异或的操作是相同为0 不同为1,即将前16位二进制全变为0,只剩下其余的二进制值);key的hashcode二进制位数小于16位的,计算出来的下标值都为0。
源码:
jdk8去掉了indexFor方法,直接在代码里写的(tab.length-1)&hash (注:类似于该种格式,可能是(n - 1) & hash ,其中n赋值了tab.length)
key获取hash方法:
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h =key.hashCode()) ^ (h >>> 16);
}