为什么我在 Java HashMap 中收到重复的密钥?

2022-09-02 11:39:23

我似乎在标准的Java HashMap中得到了重复的密钥。通过“重复”,我的意思是键通过它们的方法相等。下面是有问题的代码:equals()

import java.util.Map;
import java.util.HashMap;

public class User {
    private String userId;
    public User(String userId) { 
        this.userId = userId;
    }
    public boolean equals(User other) {
        return userId.equals(other.getUserId());
    }
    public int hashCode() {
        return userId.hashCode();
    }
    public String toString() {
        return userId;
    }

    public static void main(String[] args) {
        User arvo1 = new User("Arvo-Part");
        User arvo2 = new User("Arvo-Part");
        Map<User,Integer> map = new HashMap<User,Integer>();
        map.put(arvo1,1);
        map.put(arvo2,2);

        System.out.println("arvo1.equals(arvo2): " + arvo1.equals(arvo2));
        System.out.println("map: " + map.toString());
        System.out.println("arvo1 hash: " + arvo1.hashCode());
        System.out.println("arvo2 hash: " + arvo2.hashCode());
        System.out.println("map.get(arvo1): " + map.get(arvo1));
        System.out.println("map.get(arvo2): " + map.get(arvo2));
        System.out.println("map.get(arvo2): " + map.get(arvo2));
        System.out.println("map.get(arvo1): " + map.get(arvo1));
    }
}

下面是生成的输出:

arvo1.equals(arvo2): true
map: {Arvo-Part=1, Arvo-Part=2}
arvo1 hash: 164585782
arvo2 hash: 164585782
map.get(arvo1): 1
map.get(arvo2): 2
map.get(arvo2): 2
map.get(arvo1): 1

如您所见,两个对象上的方法正在返回,并且它们的哈希代码是相同的,但它们在 中各自形成一个不同的。此外,继续区分最近四个调用中的两个键。equals()UsertruekeymapmapUserget()

这直接与文档相矛盾:

更正式地说,如果此映射包含从键 k 到值 v 的映射,使得 (key==null ? k==null : key.equals(k)),则此方法返回 v;否则,它将返回 null。(最多可以有一个这样的映射。

这是一个错误吗?我在这里错过了什么吗?我运行的是Java版本1.8.0_92,我通过Homebrew安装。

编辑:这个问题已被标记为另一个问题的副本,但我将保持此问题不变,因为它标识了与 的看似不一致,而另一个问题假设错误在于 。希望这个问题的存在将使这个问题更容易搜索。equals()hashCode()


答案 1

问题在于您的方法。的签名是 ,但在你的情况下是 ,所以这是两个完全不同的方法,哈希映射正在调用带有参数的方法。您可以通过在等值上放置注释来验证这一点 - 它将生成编译器错误。equals()Object.equals()equals(OBJECT)equals(USER)Object@Override

等式方法应为:

  @Override
  public boolean equals(Object other) {
    if(other instanceof User){
        User user = (User) other;
        return userId.equals(user.userId);
    }

    return false;
}

作为最佳实践,您应该始终使用您覆盖的方法 - 它可以为您节省很多麻烦。@Override


答案 2

您的 equals 方法不会重写 ,并且 中的类型会在运行时被擦除,因此称为 的实际 equals 方法是 。你的等式应该更像这样:equalsMapequals(Object)

@Override
public boolean equals(Object other) {
    if (!(other instanceof User))
        return false;
    User u = (User)other;
    return userId.equals(u.userId);
}

推荐