HashMap源码分析四
HashMap源码在jdk1.8中,改动挺大,里面内容已经变的非常复杂了,后面另起博客分析。jdk1.8以前,HashMap一直是数组加链表的数据结构,在数组的某个下标位置,有多次碰撞,则使用链表数据结果存储。在jdk1.8中,引入了红黑二叉查找树的数据结构。刚开始产生碰撞时,碰撞处仍然是链表结构,当链表的长度超过源码设定值8以后,该处的链表将转为红黑二叉树。相比以前,查询效率会高很多,同时代码也变得有一定的复杂度。
HashMap的查询快速的特性是了利用了数组查询快速的特性,把key通过hash求值对应到特定的数组下标。hash碰撞对查询效率,空间使用有很大的影响。当然碰撞没法消除,只能是把碰撞概率越降越低。
idk1.2,jdk1.3的hash计算方式
int index = (hash & 0x7FFFFFFF) % tab.length;
jdk1.4的hash计算方式
static int hash(Object x) {
int h = x.hashCode();
h += ~(h << 9);
h ^= (h >>> 14);
h += (h << 4);
h ^= (h >>> 10);
return h;
}
jdk1.5的hash计算方式
private static int oldHash(int h) { h += ~(h << 9); h ^= (h >>> 14); h += (h << 4); h ^= (h >>> 10); return h; } private static int newHash(int h) { h ^= (h >>> 20) ^ (h >>> 12); return h ^ (h >>> 7) ^ (h >>> 4); } static int hash(int h) { return useNewHash ? newHash(h) : oldHash(h); } // -XX:+UseNewHashFunction or -XX:+AggressiveOpts 启动新的hash计算 private static final boolean useNewHash; static { useNewHash = false; }
jdk1.6 的hash计算方式
static int hash(int h) {
h ^= (h >>> 20) ^ (h >>> 12);
return h ^ (h >>> 7) ^ (h >>> 4);
}
jdk1.7的计算方式
final int hash(Object k) { int h = hashSeed; // 如果key对象是字符串,则换个方式生成hash值 if (0 != h && k instanceof String) { return sun.misc.Hashing.stringHash32((String) k); } // 添加生成因子 h ^= k.hashCode(); // This function ensures that hashCodes that differ only by // constant multiples at each bit position have a bounded // number of collisions (approximately 8 at default load factor). h ^= (h >>> 20) ^ (h >>> 12); return h ^ (h >>> 7) ^ (h >>> 4); }
jdk1.8的hash计算方式
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
hash求值方式是否优略,主要看hash值是否足够分散。这里可以看出jdk1.2,jdk1.3的求值方式,还挺原始,没做过多的分散处理,相对来说是需要优化的。后面几个hash求值方式,在这里目前还不好详细分析各个hash方式的优缺点。
松下问童子,言师采药去。
只言此山中,云深不知处。