覆盖哈希码()方法



>Joshua Bloch 在 Effective Java 上说:

您必须在每个覆盖 equals() 的类中重写 hashCode()。 否则将导致违反一般合同 对于 Object.hashCode(),这将阻止您的类运行 与所有基于哈希的集合正确结合,包括 HashMap、HashSet 和 Hashtable。

我重写的 equals() 方法实现了模糊分数算法来比较Match对象:

public class Match {
    private String homeTeam;
    private String awayTeam;
    public Match(String homeTeam, String awayTeam) {
        this.homeTeam = formatTeamName(homeTeam);
        this.awayTeam = formatTeamName(awayTeam);
    }
}
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Match that = (Match) o;
        final int threshold = 6;
        return (computeFuzzyScore(this.homeTeam, that.awayTeam) <= threshold || computeFuzzyScore(this.awayTeam, that.homeTeam) <= threshold) &&
                    computeFuzzyScore(this.homeTeam, that.homeTeam) > threshold && computeFuzzyScore(this.awayTeam, that.awayTeam) > threshold;
    }
    // formatTeamName(), computeFuzzyScore() have been left out for brevity.
}

这样,这些对象是相等的:

Match match0 = new Match("Man.City", "Atl.Madryt");
Match match1 = new Match("Manchester City", "Atlético Madryt");

我应该如何重写hashCode()方法为此类对象生成相同的值?

正如 M. le Rutte 和 AxelH 的答案已经说过的那样,equals 应该只对相同的对象返回 true(应该可以始终在代码之间切换并在代码中呈现相同的结果,无论使用哪个)。

解决此问题的一种方法是使用包装类,如 Java 8 中基于属性从对象列表中删除重复项的答案中所述。你这样做是为了让包装类只存储计算的模糊值,并比较和使用等于和哈希码中的值,然后你可以使用 unwrapp 来获取实际值。

另一种方法是像yshavit所说的那样做,做另一个类似于String:equalsIgnoreCase的相等

我建议您使用其他方法名称,例如fuzzyEquals而不是equals。您应该根据hashCodeequals的使用情况来查看它们。许多 Java 类(如 HashMap)在未经您同意的情况下调用这两个方法,并要求它们严格遵守自己的想法。他们的想法不是你想要什么,而是他们需要什么。它是这样的:

  • equals = homeTeam.equals && awayTeam.equals
  • hashCode = homeTeam.hashCode ^ awayTeam.hashCode

通过重命名,你(a)让HashMap和他的朋友开心,(b)避免混淆,(c)提高可读性,(d)有两种不同的方法处理两种不同的东西,你可以独立使用或进一步组合。

为了完成,您应该查看数据模型。这是暂时让你失望的人

  • Match介于两个Team之间。
  • 一个Team有一个name
  • Teamalias (0..n)。

你会有这样的想法:

public Team{
    private final String name;
    private List<String> alias;
    public Team(Sting name){ ... }
    public boolean equals(Object obj){
        // check name
    }
    public int hashCode(){
        // hash the name
    }
}

然后,只需查看以相同方式使用此类Match

Team可以提供一种方法来检查任何与String匹配的别名,如果没有匹配/在alias中找到,它将使用你的算法与name进行检查。如果这alias匹配,您可以将其添加到未来研究的List中。

这样,您就不需要每次都运行模糊算法。如果您希望用户获取与他选择的任何输入匹配的Team,这可能会很有用。

重要的部分是,如果match1 == match2那么match1.hashCode() == match2.hashCode() .假设您有一个数据库,您可以在其中存储带有 id(数字)的匹配项,那么您可以hashCode返回 id 并完成。

无需使用数据库来跟踪匹配的 ID,您就可以完成为每个团队分配一个固定长度的数字 ID,并将两个 ID 连接起来作为 hashCode 的结果。

例如,"Manchester City"团队可以是团队1"Atlético Madryt"团队2。如果哈希为 32 位长,则可以让前 16 位是团队1,后 16 位是团队2,如此表示形式所示。

// 16 bit for team 1 + 16 bit for team 2
0000000000000001        0000000000000010

这是一个有效的 32 位整数,将匹配 match1 == match2 然后match1.hashCode() == match2.hashCode()

相关内容

最新更新