>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
。您应该根据hashCode
和equals
的使用情况来查看它们。许多 Java 类(如 HashMap
)在未经您同意的情况下调用这两个方法,并要求它们严格遵守自己的想法。他们的想法不是你想要什么,而是他们需要什么。它是这样的:
- equals = homeTeam.equals && awayTeam.equals
- hashCode = homeTeam.hashCode ^ awayTeam.hashCode
通过重命名,你(a)让HashMap和他的朋友开心,(b)避免混淆,(c)提高可读性,(d)有两种不同的方法处理两种不同的东西,你可以独立使用或进一步组合。
为了完成,您应该查看数据模型。这是暂时让你失望的人
Match
介于两个Team
之间。- 一个
Team
有一个name
Team
有alias
(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()