Thinking in Java之HashMap源码分析(四)

2014-11-24 11:22:29 · 作者: · 浏览: 16
还没有Entry。
modCount++;
// 将key、value添加到i索引处。
addEntry(hash, key, value, i);
return null;
}
思考1:(未发生hash冲突情况下)

table是线性数组,是如何实现随机存储的呢?
这里是通过下面两行代码来实现的


[java]
int hash = hash(key);//注意这里的实现是jdk1.7和以前的版本有区别的
// 搜索指定hash值在对应table中的索引。
int i = indexFor(hash, table.length);

int hash = hash(key);//注意这里的实现是jdk1.7和以前的版本有区别的
// 搜索指定hash值在对应table中的索引。
int i = indexFor(hash, table.length); 通过hash方法的到key对象的哈希码,在通过indexFox()方法生成索引。具体如下:
[java]
/**产生哈希码*/
final int hash(Object k) {
int h = 0;
if (useAltHashing) {
if (k instanceof String) {
return sun.misc.Hashing.stringHash32((String) k);
}
h = hashSeed;
}

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).
/*加入高位计算,防止低位不变,高位变化是引起hash冲突*/
h ^= (h >>> 20) ^ (h >>> 12);
return h ^ (h >>> 7) ^ (h >>> 4);
}
/**产生索引,由于索引产生是不确定的,因此也就造成了HashMap顺序的不确定性。
需要注意的是不同的hash产生的索引完全有可能相同的
该方法的实现十分的巧妙,它通过h & (length-1)来的到对象保存的
索引,有可知道底层数组为2的n次方,这在速度上就有了明显的优化
*/
static int indexFor(int h, int length) {
return h & (length-1);
}

/**产生哈希码*/
final int hash(Object k) {
int h = 0;
if (useAltHashing) {
if (k instanceof String) {
return sun.misc.Hashing.stringHash32((String) k);
}
h = hashSeed;
}

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).
/*加入高位计算,防止低位不变,高位变化是引起hash冲突*/
h ^= (h >>> 20) ^ (h >>> 12);
return h ^ (h >>> 7) ^ (h >>> 4);
}
/**产生索引,由于索引产生是不确定的,因此也就造成了HashMap顺序的不确定性。
需要注意的是不同的hash产生的索引完全有可能相同的
该方法的实现十分的巧妙,它通过h & (length-1)来的到对象保存的
索引,有可知道底层数组为2的n次方,这在速度上就有了明显的优化
*/
static int indexFor(int h, int length) {
return h & (length-1);
}
通过上述代码我们可以很深刻的理解到,通过key值的hashCode方法返回的hash码,来产生

索引i。如果table[i]为null,则此处没有键值对,可以直接插入。如果table[i]!=null则此处有元素

则上面的for循环就是存储的新的元素:


[java]
for (Entry e = table[i]; e != null; e = e.next) {
Object k;
if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
V oldValue = e.value;
e.value = value;
e.recordAccess(this);
return oldValue;
}
}

for (Entry e = table[i]; e != null; e = e.next) {
Object k;
if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
V oldValue = e.value;
e.value = value;
e.recordAccess(this);
return oldValue;
}
} Tips:上述的key对象的hashCode我们应该主动的去重写,是的其和equals方法的逻辑一致性,

例如一个具体的对象作为一个key,我们通过内容判断是否相等(equals重写),这时我们应该要

主动的去重写hashCode方法,让其和equals方法一样的逻辑结果。如若不然的话,我们认为相等

的key会覆盖,却因为没有重写hashCode实际上没有覆盖!


因为实际我们用String作为key的比较多,也就没有太过注意上述情况。

下面的就是一个错误例子


[java]
package com.kiritor;

public class Student {
String name;

public Student