我们将一个String键存储在HashMap中,该HashMap是三个String字段和一个布尔字段的串联。问题是如果分隔符出现在字段值中,则可以创建重复键。
所以为了解决这个问题,根据another帖子中的建议,我计划创建一个将用作HashMap密钥的密钥类:
class TheKey {
public final String k1;
public final String k2;
public final String k3;
public final boolean k4;
public TheKey(String k1, String k2, String k3, boolean k4) {
this.k1 = k1; this.k2 = k2; this.k3 = k3; this.k4 = k4;
}
public boolean equals(Object o) {
TheKey other = (TheKey) o;
//return true if all four fields are equal
}
public int hashCode() {
return ???;
}
}
我的问题是:
答案 0 :(得分:12)
只是hashCode和equals应该没问题。 hashCode看起来像这样:
public int hashCode() {
int hash = 17;
hash = hash * 31 + k1.hashCode();
hash = hash * 31 + k2.hashCode();
hash = hash * 31 + k3.hashCode();
hash = hash * 31 + k4 ? 0 : 1;
return hash;
}
当然,假设没有一个键可以为空。通常,您可以使用0作为上述等式中空引用的“逻辑”哈希码。复合等式/哈希码的两种有用方法,需要处理空值:
public static boolean equals(Object o1, Object o2) {
if (o1 == o2) {
return true;
}
if (o1 == null || o2 == null) {
return false;
}
return o1.equals(o2);
}
public static boolean hashCode(Object o) {
return o == null ? 0 : o.hashCode();
}
在这个答案开头的哈希算法中使用后一种方法,你最终得到的结果如下:
public int hashCode() {
int hash = 17;
hash = hash * 31 + ObjectUtil.hashCode(k1);
hash = hash * 31 + ObjectUtil.hashCode(k2);
hash = hash * 31 + ObjectUtil.hashCode(k3);
hash = hash * 31 + k4 ? 0 : 1;
return hash;
}
答案 1 :(得分:10)
在Eclipse中,您可以通过Alt-Shift-S h生成hashCode和equals。
答案 2 :(得分:2)
你的hashCode()的实现并不重要,除非你把它变得非常愚蠢。你很可能只返回所有字符串哈希码的总和(截断为int),但你应该确保你解决这个问题:
如果您的哈希代码实现很慢,请考虑在实例中缓存它。根据您的关键对象的长度以及它们如何与哈希表一起使用时,您可能不希望花费超过必要的时间反复计算相同的值。如果你坚持使用Jon的hashCode()实现,可能不需要它,因为String已经为你缓存了它的hashCode()。
这是一个更普遍的建议,因为90年代中期我看到很多开发人员在慢速(甚至更糟,更改)hashCode()实现上受到刺激。
创建equals()实现时不要马虎。你的上述等于()将无效且有缺陷。首先,如果对象具有不同的哈希码,则不需要比较值。如果你得到一个null作为参数,你也应该返回false(而不是空指针异常)。
规则很简单,this page会引导您完成这些规则。
修改强> 我还要问一件事......你说“如果分隔符出现在字段值中,则问题是可以创建重复键”。这是为什么? 如果格式是键+分隔符+键+分隔符+键,那么键中是否有一个或多个分隔符并不重要,除非你真的不太喜欢两个键的组合,在这种情况下你可能应该选择另一个分隔符(在unicode中有很多可供选择)。
无论如何,Jon在下面的评论中是正确的......不要做“缓存”,直到你证明这是一件好事“。这总是很好的做法。
答案 3 :(得分:2)
要求Eclipse 3.5为您创建哈希码和等于方法:)
答案 4 :(得分:2)
这是一个结构良好的等于equals和hashCode的类应该如下所示:(使用intellij idea生成,启用空检查)
class TheKey {
public final String k1;
public final String k2;
public final String k3;
public final boolean k4;
public TheKey(String k1, String k2, String k3, boolean k4) {
this.k1 = k1;
this.k2 = k2;
this.k3 = k3;
this.k4 = k4;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
TheKey theKey = (TheKey) o;
if (k4 != theKey.k4) return false;
if (k1 != null ? !k1.equals(theKey.k1) : theKey.k1 != null) return false;
if (k2 != null ? !k2.equals(theKey.k2) : theKey.k2 != null) return false;
if (k3 != null ? !k3.equals(theKey.k3) : theKey.k3 != null) return false;
return true;
}
@Override
public int hashCode() {
int result = k1 != null ? k1.hashCode() : 0;
result = 31 * result + (k2 != null ? k2.hashCode() : 0);
result = 31 * result + (k3 != null ? k3.hashCode() : 0);
result = 31 * result + (k4 ? 1 : 0);
return result;
}
}
答案 5 :(得分:1)
你看过specifications of hashCode()
了吗?也许这会让你更好地了解函数应返回的内容。
答案 6 :(得分:1)
我不知道这是否适合您,但apache commons库提供了MultiKeyMap的实现
答案 7 :(得分:1)
对于hashCode,您可以使用类似
的内容k1.hashCode()^ k2.hashCode()^ k3.hashCode()^ k4.hashCode()
XOR是熵保留的,这比以前的建议更好地结合了k4的hashCode。从k4获取一位信息意味着如果所有复合键具有相同的k1,k2,k3并且只有不同的k4,则您的哈希码将全部相同,并且您将获得退化的HashMap。
答案 8 :(得分:1)
我认为你的主要关注点是速度(根据你原来的帖子)?你为什么不确保使用一个(少数几个)字段值中没有出现的分隔符?然后你可以使用连接创建String键并取消所有这些'key-class'hocus pocus。闻起来像是对我严重过度工程。