Java HashMap, hashCode() equals() - 多个key如何保持一致?

Java HashMap, hashCode() equals() - how to be consistent with multiple keys?

我有一个 ID class。它指的是对一个独特的人的识别。 ID 有两种形式。用户名和用户号。如果用户名匹配或用户号码匹配,则 ID 指的是同一个人。我想使用 ID class 作为哈希图中的键来查找人员。我覆盖了 equals 方法以明确如果两个 ID 相等,则意味着它们指的是同一个人。我假设我需要重写 hashCode() 方法,因为我想要一个 的 hashmap。但我不知道怎么办。如何使 ID class 成为我的 HashMap 可接受的键?

public final class ID {

    // These are all unique values. No 2 people can have the same username or id value.
    final String username_;
    final long idNumber_;

    public ID(String username, Byte[] MACaddress, long idNumber) {
    username_ = username;
    idNumber_ = idNumber;
    }

    /**
     * Checks to see if the values match. 
     * If either username or idNumber matches, then both ID's refer to the same person.
     */
    public boolean equals(Object o) {
    if (!(o instanceof ID)) {
        return false;
    }
    ID other = (ID) o;
    if (this.username_ != null && other.username_ != null) {
        if (this.username_.equals(other.username_)) {
            return true;
        }
    }
    if (this.idNumber_ > 0 && other.idNumber_ > 0) {
        if(this.idNumber_ == other.idNumber_) {
            return true;
        }
    }
    return false;
    }
}

跟进:如果我想为唯一社会保险号添加第三个字段怎么办?这将如何改变我的个人查找哈希图和 ID class?

请注意,hashCode 的维基百科页面说:

The general contract for overridden implementations of this method is that they behave in a way consistent with the same object's equals() method: that a given object must consistently report the same hash value (unless it is changed so that the new version is no longer considered "equal" to the old), and that two objects which equals() says are equal must report the same hash value.

更新

迄今为止的最佳解决方案: 为每个不可变的唯一标识符创建一个单独的 HashMap,然后将所有 HashMap 包装在一个包装器对象中。

我认为您的问题确实远远超出了 hashCode 的简单实现 - 它实际上是关于何时应将两个 ID 视为相等的设计。

如果您有多个字段 所有 其中都是唯一的,那么您实际上处于一个棘手的情况。忽略 MAC 地址部分,ID 代码本身没有任何内容可以停止:

ID a = new ID("a", 0);
ID b = new ID("b", 0);
ID c = new ID("a", 1);

现在按照您的规则,这三个 ID 不应该都能够共存 - 因为它们在某种程度上都是平等的。 (ab 同名,ac 同号。)

此外,号码和姓名似乎都是可选的 - 根据我的经验,这可能最终会很痛苦。

保持 class 原样 可能 仍然有意义,但 "No two entities can have the same name" 和 "No two entities can have the same number" 周围的规则不是与 "no two entities can have the same ID" 完全相同,因此您必须单独强制执行。

您完全有可能希望为 "entities by name" 保留 HashMap<String, Entity>,为 "entities by number" 保留 HashMap<Integer, Entity>。然后我会在 ID 中实现 equalshashCode 来检查 complete 相等性——这样在仅通过 [=35= 查找 ID 之后]一个部分ID,你可以检查你找到的ID是否真的是正确的。

或者,将您的身份方案更改为只有一个部分,这样您就永远不必担心这个问题。即使您有某种 "secondary identifier",也值得决定其中的 一个 作为您的主要标识符,您通常使用它来查找实体、存储它们等 - 然后您只需要在主要标识符和次要标识符之间进行次要映射(无论是在内存中还是在数据库中等)。如果你所有的 "real" 标识符都是可选的,你甚至可能想要一个 保证 存在的代理标识符,然后它和次要标识符之间的所有映射都可以是选修的。 (它们甚至可能是可变的 - 例如,您可以 "discover" 并稍后针对现有实体记录社会安全号码。)