当我们使用对象类型作为 HashMap 的 key的时候,我们往往希望两个对象里的某一个或者某几个成员变量相等,则这两个对象是相等的,而不是说采用 Java 默认的地址空间一致并且值也相等才表示两个对象相等。
假设我们有一个对象 Node表示一个点,它有两个成员变量 a 和 b
class Node {
int a; // 表示从a到b
int b;
public Node(int a, int b) {
this.a = a;
this.b = b;
}
}
现在我们创建一个 HashMap,并且在 map 里存入点(1,1)然后我们获取点(1,1)对应的权重。
public class Test {
public static void main(String[] args) {
HashMap<Node, Integer> map = new HashMap<>();
Node node = new Node(1, 1);
map.put(node, 2);
System.out.println(map.get(new Node(1, 1)));
}
}
我们运行代码可以发现 map 返回的值是 null 而不是 2,这是因为我们在获取的时候又用 new 关键字创建了一个对象,然后在堆区开辟了一块新的内存空间,这导致这个“新的对象”之前并没有存在hash表里。
然后我们重写 Node 对象的 equals 方法,
class Node {
int a; // 表示从a到b
int b;
public Node(int a, int b) {
this.a = a;
this.b = b;
}
@Override
public boolean equals(Object obj) {
Node node = (Node) obj;
if (this.a == node.a && this.b == node.b) {
return true;
}
return false;
}
}
这时我们再执行程序,发现依然是 null,这是为什么呢?
当我们重写了 equals 方法之后再区比较这两个对象,他们肯定是相等的。那问题就出在了 HashMap 身上,因为 HashMap 对 key 做 hash 的过程是通过调用 hash() 函数得到一个 int 型的随机数,而这个 hash 函数的实现是和对象创建的内存地址强相关的。也就是说即使我们重写了 equals 函数,但是这两个对象本身内存地址还是不一样的,这就导致 HashMap 在 hash 的时候还是得到了不同的值,它再用 equals 去找相同的对象自然也就找不到了。
所以我们还需要重写 Node 的 hashCode 函数,使得两个对象的 hash 值和它们地址没有任何关系。最终 Node 这个类的形式如下
class Node {
int a; // 表示从a到b
int b;
public Node(int a, int b) {
this.a = a;
this.b = b;
}
@Override
public boolean equals(Object obj) {
Node node = (Node) obj;
if (this.a == node.a && this.b == node.b) {
return true;
}
return false;
}
@Override
public int hashCode() {
return (a << 16) | b;
}
}
这时我们再运行程序就可以得到我们想要的值了
Last modified on 2023-04-21