我使用的是Java 7,下面有以下类。我正确地实现了equals
和hashCode
,但问题是equals
在下面的main方法中返回false
,hashCode
为两个对象返回相同的哈希码。我可以让更多的眼睛看这堂课,看看我在这里做错了吗?
更新:我用我自己的哈希函数Objects.hash
替换了我调用chamorro.hashCode() + english.hashCode() + notes.hashCode()
方法的行。它返回一个不同的哈希码,这是hashCode
当两个对象不同时应该做的事情。 Objects.hash
方法是否已损坏?
非常感谢您的帮助!
import org.apache.commons.lang3.StringEscapeUtils;
public class ChamorroEntry {
private String chamorro, english, notes;
public ChamorroEntry(String chamorro, String english, String notes) {
this.chamorro = StringEscapeUtils.unescapeHtml4(chamorro.trim());
this.english = StringEscapeUtils.unescapeHtml4(english.trim());
this.notes = notes.trim();
}
@Override
public boolean equals(Object object) {
if (!(object instanceof ChamorroEntry)) {
return false;
}
if (this == object) {
return true;
}
ChamorroEntry entry = (ChamorroEntry) object;
return chamorro.equals(entry.chamorro) && english.equals(entry.english)
&& notes.equals(entry.notes);
}
@Override
public int hashCode() {
return java.util.Objects.hash(chamorro, english, notes);
}
public static void main(String... args) {
ChamorroEntry entry1 = new ChamorroEntry("Åguigan", "Second island south of Saipan. Åguihan.", "");
ChamorroEntry entry2 = new ChamorroEntry("Åguihan", "Second island south of Saipan. Åguigan.", "");
System.err.println(entry1.equals(entry2)); // returns false
System.err.println(entry1.hashCode() + "\n" + entry2.hashCode()); // returns same hash code!
}
}
答案 0 :(得分:12)
实际上,你恰好触发了纯粹的巧合。 :)
Objects.hash
恰好通过连续添加每个给定对象的哈希码然后将结果乘以31来实现,而String.hashCode
对每个字符执行相同的操作。巧合的是,"英语"你使用的字符串恰好出现在字符串末尾的一个偏移处,因为" Chamorro"字符串,所以一切都完美取消。恭喜!
尝试使用其他字符串,您可能会发现它按预期工作。正如其他人已经指出的那样,严格来说,这种效果实际上并不是错误的,因为即使它们所代表的对象不相等,哈希码也可能正确地发生碰撞。如果有的话,尝试找到更有效的哈希可能是值得的,但我认为在现实情况下它应该是必要的。
答案 1 :(得分:6)
不要求不等对象必须具有不同的hashCodes。期望等对象具有相等的hashCodes,但不禁止散列冲突。 return 1;
将是hashCode的完全合法的实现,如果不是非常有用。
可能只有32位可能的哈希码,并且有无限数量的可能对象,毕竟:)有时会发生冲突。
答案 2 :(得分:4)
HashCode是32位int值,总是存在冲突的可能性(两个对象的哈希码相同),但它很少/巧合。 你的例子就是其中一个非常巧合的。这是解释。
当您致电Objects.hash
时,它会在内部使用以下逻辑调用Arrays.hashCode()
:
public static int hashCode(Object a[]) {
if (a == null)
return 0;
int result = 1;
for (Object element : a)
result = 31 * result + (element == null ? 0 : element.hashCode());
return result;
}
对于你的3个param hashCode,结果如下:
31 * (31 * (31 *1 +hashOfString1)+hashOfString2) + hashOfString3
为你的第一个对象。单个字符串的哈希值为:
chamorro - > 1140493257 英语 - > 1698758127 笔记 - > 0
对于第二个对象:
chamorro - > 1140494218 英语 - > 1698728336 笔记 - > 0
如果您注意到,两个对象中前两个哈希码值都不同。
但是当它将最终的哈希码计算为:
int hashCode1 = 31*(31*(31+1140493257) + 1698758127)+0;
int hashCode2 = 31*(31*(31+1140494218) + 1698728336)+0;
巧合的是,它会产生相同的哈希码1919283673
,因为int
以32位存储。
使用以下代码段验证您自己的理论:
public static void main(String... args) {
ChamorroEntry entry1 = new ChamorroEntry("Åguigan",
"Second island south of Saipan. Åguihan.", "");
ChamorroEntry entry2 = new ChamorroEntry("Åguihan",
"Second island south of Saipan. Åguigan.", "");
System.out.println(entry1.equals(entry2)); // returns false
System.out.println("Åguigan".hashCode());
System.out.println("Åguihan".hashCode());
System.out.println("Second island south of Saipan. Åguihan.".hashCode());
System.out.println("Second island south of Saipan. Åguigan.".hashCode());
System.out.println("".hashCode());
System.out.println("".hashCode());
int hashCode1 = 31*(31*(31+1140493257) + 1698758127)+0;
int hashCode2 = 31*(31*(31+1140494218) + 1698728336)+0;
System.out.println(entry1.hashCode() + "\n" + entry2.hashCode());
System.out.println(getHashCode(
new String[]{entry1.chamorro, entry1.english, entry1.notes})
+ "\n" + getHashCode(
new String[]{entry2.chamorro, entry2.english, entry2.notes}));
System.out.println(hashCode1 + "\n" + hashCode2); // returns same hash code!
}
public static int getHashCode(Object a[]) {
if (a == null)
return 0;
int result = 1;
for (Object element : a)
result = 31 * result + (element == null ? 0 : element.hashCode());
return result;
}
如果你使用一些不同的字符串参数,希望它会导致不同的hashCode。
答案 3 :(得分:1)
两个不相等的对象不必具有不同的哈希值,重要的是对两个相等的对象使用相同的哈希值。
我可以像这样实现hashCode():
public int hashCode() {
return 5;
}
它将保持正确(但效率低下)。