面试时,有时会被问到HashMap是如何解决hashcode冲突的。
主要使用 “拉链法”
public V put(K key, V value) {
if (key == null)
return putForNullKey(value);
int hash = hash(key.hashCode());
int i = indexFor(hash, table.length);
for (Entry<K,V> e = table[i]; e != null; e = e.next) {
Object k;
//判断当前确定的索引位置是否存在相同hashcode和相同key的元素,如果存在相同的hashcode和相同的key的元素,那么新值覆盖原来的旧值,并返回旧值。
//如果存在相同的hashcode,那么他们确定的索引位置就相同,这时判断他们的key是否相同,如果不相同,这时就是产生了hash冲突。
//Hash冲突后,那么HashMap的单个bucket里存储的不是一个 Entry,而是一个 Entry 链。
//系统只能必须按顺序遍历每个 Entry,直到找到想搜索的 Entry 为止——如果恰好要搜索的 Entry 位于该 Entry 链的最末端(该 Entry 是最早放入该 bucket 中),
//那系统必须循环到最后才能找到该元素。
if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
V oldValue = e.value;
e.value = value;
return oldValue;
}
}
modCount++;
addEntry(hash, key, value, i);
return null;
}
如图所示
例如:A、B、C ,A计算出来的索引为0,此时存储为table[0],此时B计算出来的也是0,那么就会存储为B.entry=A,table[0]=B,此时C也为0,那么就会存储为C.entry = B,table[0]=C,这样就会把A、B、C都存储进去。
具体说下是如何比较的(显示比较hashcode,如何hashcode相等,再去比较equals方法)
写一个测试类
class A {
@Override
public boolean equals(Object obj) {
System.out.println("判断equals");
return false;
}
@Override
public int hashCode() {
System.out.println("判断hashcode");
return 1;
}
}
public class Test {
public static void main(String[] args) {
Map<A,Object> map = new HashMap<A, Object>();
map.put(new A(), new Object());
map.put(new A(), new Object());
System.out.println(map.size());
}
}
输出结果
判断hashcode
判断hashcode
判断equals
2
说明:因为第一个map中是没有hashcode值的,所以只比较了hashcode,而第二次,map中存在相同的hashcode值,所以去比较对象,对象返回的是false,所以永远不会相同,这样就存储进去了。
如果将equals改为true,
class A {
@Override
public boolean equals(Object obj) {
System.out.println("判断equals");
return true;
}
@Override
public int hashCode() {
System.out.println("判断hashcode");
return 1;
}
}
此时再去执行,就会是
判断hashcode
判断hashcode
判断equals
1
也验证了如下结果:
1、如果两个对象equals,Java运行时环境会认为他们的hashcode一定相等。
2、如果两个对象不equals,他们的hashcode有可能相等。
3、如果两个对象hashcode相等,他们不一定equals。
4、如果两个对象hashcode不相等,他们一定不equals。