如果我们在类中只重写hashCode()并在Set中使用它,会发生什么



这可能不是真实的场景,只是想知道会发生什么,下面是代码。

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

由于我没有重写equals方法,默认实现将返回false。那么,值为"a"的Object将被保存在哪里呢?在保存哈希代码为97的前一个Object的同一个bucket中?还是会创建新的bucket?有人知道它将如何在内部存储吗?

/* 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]

HashCode&Equals方法

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

James Large的回答是不正确的,或者说是误导性的(部分也不正确)。我会解释的。

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

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

  • 如果根据equals(Object)方法,两个对象相等,那么对这两个对象中的每一个调用hashCode方法必须产生相同的整数结果
  • 如果根据equals(java.lang.Object)方法,两个对象不相等,那么对这两个对象中的每一个调用hashCode方法必须产生不同的整数结果,这是不需要的。然而,程序员应该意识到,为不相等的对象生成不同的整数结果可能会提高哈希表的性能

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

此外,说hashCode函数是做任何事情的有效方法也是极不正确的。这一切都取决于实现,但字符串的hashCode的默认实现效率非常低,因为字符串越来越大。它将根据String的每个字符执行计算,所以如果您使用大型String作为键,那么这将变得非常低效;如果你有大量的水桶。

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

不包含重复元素的集合。

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

不过,一般来说,最好的做法是,如果覆盖hashCode函数,也会覆盖equals函数(如果我没有错的话,如果你选择不覆盖,这就违反了约定)。

您可以简单地将hashcode和equals方法假设为2D搜索,如:-

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

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");

现在,如果你没有覆盖hashcode和equals,那么在将所有对象放置到第5行之后,如果你将obj5作为默认hashcode放在映射中,你会得到不同的hashcode,所以行(Bucket将不同)。所以在运行时内存中,它将像这样存储。

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

现在,如果您创建相同的对象,如:-obj obj6=新的obj("hassu",1);如果你在类似地图的中搜索这个值

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

尽管具有相同内容的密钥(obj1)可用,但您将分别获得false和null。现在,如果只重写equals方法。并且执行相同的内容搜索关键字也会得到Null,因为obj6的哈希代码不同,并且在该哈希代码中你找不到任何关键字。现在,如果您只重写hashCode方法。

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

Set的行为会有所不同。

独一无二是不会发生的。因为unique将通过hashcode和equals方法实现
输出将喜欢这个s=[A,A,b,1],而不是早期的。

除此之外,删除并包含所有内容都不起作用。

不看代码。。。

散列码的全部目的是加快测试两个对象是否相等的过程。测试两个大型复杂对象是否相等可能代价高昂,但比较它们的哈希码非常容易,而且哈希码可以预先计算。

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

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


添加信息:哇!我真的很怀念这里的树——思考hashCode()的目的,而没有把它放在HashMap的上下文中。如果m是具有N个条目的映射,并且k是密钥;调用m.get(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)

相关内容

最新更新