如果我们只覆盖类中的hashCode()并在Set中使用它会发生什么?

这可能不是现实世界的场景,但只是想知道发生了什么,下面是代码。

我正在创建一组UsingSet类的对象。 根据Java中的哈希概念,当我第一次添加包含“a”的对象时,它将创建一个带有哈希码97的桶并将对象放入其中。 当它遇到一个带有“a”的对象时,它会在UsingSet类中调用重写的hashcode方法,它将获得hashcode 97,那么下一步是什么?

由于我没有覆盖equals方法,默认实现将返回false。 那么具有值“a”的Object将保留在哪个存储区中的前一个对象保存的同一个存储区中? 还是会创造新的桶? 有谁知道它将如何存储在内部?

 /* package whatever; // don't place package name! */ import java.util.*; import java.lang.*; import java.io.*; class UsingSet { String value; public UsingSet(String value){ this.value = value; } public String toString() { return value; } public int hashCode() { int hash = value.hashCode(); System.out.println("hashcode called" + hash); return hash; } public static void main(String args[]) { java.util.Set s = new java.util.HashSet(); s.add(new UsingSet("A")); s.add(new UsingSet("b")); s.add(new UsingSet("a")); s.add(new UsingSet("b")); s.add(new UsingSet("a")); s.add(new Integer(1)); s.add(new Integer(1)); System.out.println("s = " + s); } } 

输出是:

 hashcode called65 hashcode called98 hashcode called97 hashcode called98 hashcode called97 s = [1, b, b, A, a, a] 

詹姆斯大回答是不正确的,或者说具有误导性(并且部分不正确)。 我会解释。

如果两个对象根据equals()方法相等,则它们也必须具有相同的哈希码。 如果两个对象具有相同的哈希码,则它们也不必相等。

以下是java.util.Object文档中的实际措辞:

  • 如果两个对象根据equals(Object)方法相等,则对两个对象中的每一个调用hashCode方法必须生成相同的整数结果。
  • 如果两个对象根据equals(java.lang.Object)方法不相等,则不需要在两个对象中的每一个上调用hashCode方法必须生成不同的整数结果。 但是,程序员应该知道为不等对象生成不同的整数结果可能会提高哈希表的性能。

确实,如果两个对象没有相同的哈希值,那么它们就不相等。 但是,散列不是检查相等性的一种方法 – 所以说它是检查相等性的更快方法是非常不正确的。

另外,说hashCode函数是一种有效的方法也是非常不正确的。 这完全取决于实现,但是当String变大时,字符串的hashCode的默认实现效率非常低。 它将根据String的每个char执行计算,因此如果您使用大型字符串作为键,那么这将变得非常低效; 更多,如果你有大量的水桶。

在Map(HashSet内部使用HashMap)中,存在桶,每个桶中都有一个链表。 Java使用hashCode()函数来找出它所属的桶(它实际上将修改哈希值,具体取决于存在多少桶)。 由于两个对象可以共享相同的哈希,因此它将依次遍历链表,检查equals()方法以查看该对象是否重复。 根据java.util.Set文档:

不包含重复元素的集合。

因此,如果它的hashCode()将其引导到一个存储桶,其中该存储桶包含一个.equals()计算结果为true的Object,那么前一个Object将被新的Object覆盖。 您可以在此处查看更多信息: Java HashMap如何使用相同的哈希代码处理不同的对象?

一般来说,最好是如果你覆盖hashCode函数,你也会覆盖equals函数(如果我没有弄错,如果你选择不这样做会打破合约)。

HashCode和Equals方法

  1. 仅覆盖HashCode,使用默认值Equals:只有对同一对象的引用才会返回true。 换句话说,通过调用equals方法,您期望相等的那些对象将不相等。
  2. 只有覆盖等于,使用默认的HashCode: HashMap或HashSet中可能存在重复项。 我们编写equals方法并期望{"abc", "ABC"}等于。 但是,在使用HashMap时,它们可能出现在不同的存储桶中,因此contains()方法不会相互检测它们。

Set的行为会有所不同。

独特性不会发生。 因为hashcode和equals方法都可以实现unique。
输出将被喜欢这个s = [A,a,b,1]而不是早期的。

除了删除和包含所有不会工作。

不看你的代码……

哈希码的重点是加快测试两个对象是否相等的过程。 测试两个大的复杂对象是否相等可能代价很高,但是比较它们的哈希码非常容易,并且可以预先计算哈希码。

规则是:如果两个对象没有相同的哈希码,则意味着它们不相等。 无需进行昂贵的平等测试。

那么,标题中问题的答案:如果你定义一个equals()方法,说对象A等于对象B,你定义一个hashCode()方法,说对象A 等于对象B(即,它说它们有不同的哈希码),然后你将这两个对象交给某个关心它们是否相等的库(例如,如果你将它们放在哈希表中),那么库的行为就是未定义(即可能是错误的 )。


补充信息:哇! 我真的很想念这里的树林 – 想想hashCode()的目的,而不是把它放在HashMap的上下文中。 如果m是具有N个条目的Map,则k是键; 调用m.get(k)的目的是什么? 显然,目的是在地图中搜索其键等于 k的条目。

如果没有发明哈希码和哈希映射怎么办? 假设密钥具有自然的总顺序,那么您可以做的最好的事情是搜索TreeMap,将给定的密钥与O(log(N))其他密钥进行比较。 在最坏的情况下,如果密钥没有顺序,则必须将给定的密钥与映射中的每个密钥进行比较,直到找到匹配项或对其进行全部测试。 换句话说, m.get(k)的复杂度将是O(N)。

当m是HashMap时, m.get(k)的复杂度是O(1),无论键是否可以被排序。

所以,我搞砸了哈希码的重点是加快测试两个对象的相同程度。 它实际上是用一其他对象测试一个对象是否相等。 这就是比较哈希码不仅仅有所帮助的地方; 它有助于数量级……

如果 k.hashCode()k.equals(o)方法遵守规则: j.hashCode()!=k.hashCode()暗示!j.equals(k)

您可以将hashcode和equals方法假设为2D搜索,如: –

其中Hashcode是行,而对象列表是列。 考虑以下类结构。

 public class obj { int Id; String name; public obj(String name,int id) { this.id=id; this.name=name; } } 

现在,如果您创建这样的对象: –

 obj obj1=new obj("Hassu",1); obj obj2=new obj("Hoor",2); obj obj3=new obj("Heniel",3); obj obj4=new obj("Hameed",4); obj obj5=new obj("Hassu",1); 

并将此对象放在地图中,如下所示: –

  HashMap hMap=new HashMap(); 1. hMap.put(obj1,"value1"); 2. hMap.put(obj2,"value2"); 3. hMap.put(obj3,"value3"); 4. hMap.put(obj4,"value4"); 5. hMap.put(obj5,"value5"); 

现在,如果你没有覆盖哈希码和equals然后将所有对象放到第5行之后如果你把obj5放在地图中作为By Default HashCode你得到不同的hashCode所以行(Bucket将是不同的)。 所以在运行时内存中它会像这样存储。

 |hashcode | Objects |-----------| --------- |000562 | obj1 |000552 | obj2 |000588 | obj3 |000546 | obj4 |000501 | obj5 

现在如果你创建相同的对象像: – obj obj6 = new obj(“hassu”,1); 如果你在map.like中搜索这个值

 if(hMap.conaints(obj6)) or hMpa.get(obj 6); 

虽然具有相同内容的密钥(obj1)可用,但您将分别获得false和null。 现在,如果您只覆盖equals方法。 并且执行相同的内容搜索键也将得到Null,因为obj6的HashCode是不同的,并且在该hashcode中你找不到任何键。 现在,如果您只覆盖hashCode方法。

您将获得相同的存储桶(HashCode行),但内容无法检查,它将采用超级对象类的引用检查实现。 在这里,如果你搜索关键hMap.get(obj6),你将获得正确的哈希码: – 000562但是因为obj1和obj6的引用不同,你将得到null。