equals和hashCode:Objects.hash方法被破坏了吗?

我使用的是Java 7,下面有以下类。 我正确地实现了equalshashCode ,但问题是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! } } 

实际上,你恰好触发了纯粹的巧合。 🙂

Objects.hash恰好通过连续添加每个给定对象的哈希码然后将结果乘以31来实现,而String.hashCode对其每个字符执行相同的操作。 巧合的是,你使用的“英语”字符串的差异恰好出现在字符串末尾的一个偏移处,与“Chamorro”字符串中的差异相同,所以一切都完全取消了。 恭喜!

尝试使用其他字符串,您可能会发现它按预期工作。 正如其他人已经指出的那样,严格来说,这种效果实际上并不是错误的,因为即使它们所代表的对象不相等,哈希码也可能正确地发生碰撞。 如果有的话,尝试找到更有效的哈希可能是值得的,但我认为在现实情况下它应该是必要的。

不要求不等对象必须具有不同的hashCodes。 期望等对象具有相等的hashCodes,但不禁止散列冲突。 return 1; 如果不是非常有用,那将是hashCode的完全合法的实现。

毕竟,只有32位可能的哈希码和无限数量的可能对象:)有时会发生冲突。

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 english – > 1698758127 notes – > 0

而对于第二个对象:

chamorro – > 1140494218 english – > 1698728336笔记 – > 0

如果您注意到,两个对象中的哈希码的前两个值是不同的。

但是当它将最终的哈希码计算为:

  int hashCode1 = 31*(31*(31+1140493257) + 1698758127)+0; int hashCode2 = 31*(31*(31+1140494218) + 1698728336)+0; 

巧合的是,它导致相同的哈希码1919283673因为int以32位存储。

使用以下代码段validation您自己的理论:

  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。

两个不相等的对象不必具有不同的哈希值,重要的是对两个相等的对象使用相同的哈希值。

我可以像这样实现hashCode():

 public int hashCode() { return 5; } 

它会保持正确(但效率低下)。