Override equals and hashCode method in HashMap | Java使用HashMap需要重写equals和hashCode方法的场景

Albert Wang / 2023-04-21 / 300 Words/has been Read   Times


当我们使用对象类型作为 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表里。

image-20230422205709316

然后我们重写 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,这是为什么呢?

image-20230422210138546

当我们重写了 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;
    }    
}

这时我们再运行程序就可以得到我们想要的值了

image-20230422211341683

Last modified on 2023-04-21