Hashcode()和Equals()的区别

4

我有以下这两个类...

class Emp //implements Comparable
{
      String name,job;
      public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getJob() {
        return job;
    }
    public void setJob(String job) {
        this.job = job;
    }
    public int getSalary() {
        return salary;
    }
    public void setSalary(int salary) {
        this.salary = salary;
    }
    int salary;
      public Emp(String n,String j,int sal)
      {
         name=n;
         job=j;
         salary=sal;
       }
      public void display()
      {
        System.out.println(name+"\t"+job+"\t"+salary);
       }



  public boolean equals(Object o)
      {

         Emp p=(Emp)o;
          return this.name.equals(p.name)&&this.job.equals(p.job) &&this.salary==p.salary;
       }
  /* public int hashCode()
       {
          return name.hashCode()+job.hashCode()+salary;
       }
     */

      /* public int compareTo(Object o)
       {
          Emp e=(Emp)o;
          return this.name.compareTo(e.name);
           //return this.job.compareTo(e.job);
        //   return this.salary-e.salary;

        }*/
} 

另一个是...

import java.util.*;
class EmpHsDemo
{
      public static void main(String arg[])
      {
          HashSet set=new HashSet();
          set.add(new Emp("Ram","Trainer",34000));
          set.add(new Emp("Ram","Trainer",34000));
          set.add(new Emp("Ravi","Administrator",44000));
          set.add(new Emp("Sachin","Programmer",24000));
          set.add(new Emp("Priyanka","Manager",54000));
          set.add(new Emp("Anupam","Programmer",34000));
          set.add(new Emp("Sachin","Team Leader",54000));
          System.out.println("There are "+set.size()+" elements in the set.");
          System.out.println("Content of set are : ");
          Iterator itr=set.iterator();
          while(itr.hasNext())
          {
            Emp e=(Emp)itr.next();
            System.out.print(e.hashCode()+"\t");   
            e.display();
          }   


          System.out.println("**********************************");
        Emp e1=new Emp("Ravi","Administrator",44000);
        System.out.println("Removing following Emp from the set...");
        System.out.print(e1.hashCode()+"\t");
        e1.display();
        set.remove(e1);
        System.out.println("No. of elements after removal "+set.size());
       /* Emp e2=new Emp("Anupam","Programmer",34000);
        System.out.println("Searching following Emp in the set...");
        System.out.print(e2.hashCode()+"\t");
        e2.display();
        System.out.println("Results of searching is : "+set.contains(e2));*/
      }
}

现在我正在进行一项关于IT技术的研究。

  1. 如果我注释掉hashcode()方法但不注释equals()方法,那么它将允许重复项,比如Ram会显示两次并附带内存地址,结果如下:
There are 7 elements in the set.
Content of set are : 
374283533   Priyanka    Manager 54000
1660364311  Ram Trainer 34000
1340465859  Ravi    Administrator   44000
2106235183  Sachin  Programmer  24000
2031692173  Ram Trainer 34000
603737068   Anupam  Programmer  34000
148669801   Sachin  Team Leader 54000
**********************************
Removing following Emp from the set...
1807500377  Ravi    Administrator   44000
No. of elements after removal 7

如果我取消注释 hashcode() 方法和 equals() 方法,我会得到以下结果。
There are 6 elements in the set.
Content of set are : 
1546622676  Sachin  Team Leader 54000
-302767206  Anupam  Programmer  34000
149315535   Ravi    Administrator   44000
199998062   Sachin  Programmer  24000
1407883922  Priyanka    Manager 54000
597555555   Ram Trainer 34000
**********************************
Removing following Emp from the set...
149315535   Ravi    Administrator   44000
No. of elements after removal 5

如果我仅注释掉equals()方法而不是hashcode(),那么我会得到以下结果。
There are 7 elements in the set.
Content of set are : 
1546622676  Sachin  Team Leader 54000
-302767206  Anupam  Programmer  34000
149315535   Ravi    Administrator   44000
199998062   Sachin  Programmer  24000
1407883922  Priyanka    Manager 54000
597555555   Ram Trainer 34000
597555555   Ram Trainer 34000
**********************************
Removing following Emp from the set...
149315535   Ravi    Administrator   44000
No. of elements after removal 7

请指导一下这三种方法背后的原因...!

请参见:https://dev59.com/wHRC5IYBdhLWcg3wFdJx - Richard Sitze
5个回答

7

你所发布的所有信息都不值得阅读。

Joshua Bloch认为hashCode和equals应该一起被重写。在这里是如何做到的,没有例外。 点击这里


3
是和不是。一方面,是的 - 这是Java中的一个绝对事实之一。另一方面,坚定而响亮地否定 - 在应用之前,你应该始终尝试理解某个东西的工作原理,而不仅仅基于权威声明。 - pafau k.
我理解并同意在这种情况下的权威。如果你是投票反对的人,我会说你是错误的,而不是我。我认为Java集合API的作者,在这种情况下足够有权威,他知道他的API需要什么。由于OP正在使用Java集合,他/她应该遵循建议。 - duffymo
@pafau 我同意我们应该在权威陈述之外提供解释。据看来,duffy也同意,因为他链接了《Effective Java》中解释哈希码/相等性教条背后原因的章节。他只是没有明确说“第8条明确处理哈希码/相等性”,但老实说,整个章节对于所有Java程序员来说都是必不可少的。 - corsiKa

5
一个HashSet拥有多个桶,它使用hashCode()来确定一个元素属于哪个桶,然后在该桶内,它使用equals()来查找该元素是否存在于该桶中。
这些方法的默认实现是:hashCode()使用系统标识哈希码,这可能是它的内存位置,但唯一性在于系统将尝试使用所有“合理可行”的方式使没有两个对象具有相同的系统标识哈希码,而equals比较两个对象的内存位置。(请注意,系统标识哈希码不能保证产生唯一的哈希值;而是极力努力为您提供唯一的哈希值。有关此问题的更多信息,请参见Java Bug 6321873。)
因此,了解了这些知识,根据各种方法的不同实现或默认实现,可以预测出不同的处理方法。
在您的第一种情况中,"Ram"出现在两个不同的桶中,因此两个"Ram"对象从未相互比较。它们使用以下代码进行比较(至少在Sun实现中)
if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {

这是在遍历桶中的每个对象。它们的哈希值不相等,因此永远不会检查确定对象本身是否等于提供的对象。
在第二种情况下,它按照您的期望工作,注意具有两个不同职位和薪水的人将被视为不同的人。
在第三种情况下,Ram两次进入同一个桶。但由于使用默认的equals方法,并且这两个对象是不同的对象,因此它被添加了第二次。
还值得再次强调一点,duffymo的答案完全忽略了您的问题;在99.9%的情况下,我会说这是错误的做法,我们应该首先了解您的问题范围。这是极少数情况之一,其中忽略您的问题是正确的选择。您永远不应该只覆盖hashcode和equals中的一个。您应该始终不覆盖或同时覆盖两个。

你的第二句话是不正确的。系统标识哈希码并不能保证对于每个对象都是唯一的。规范并没有强制执行任何这样的约束。 - Robin
这实际上是我的第三个句子 :-). 我其实没有意识到它没有被指定。我已经添加了必要的信息。谢谢! - corsiKa

3

1.两个对象相等时,它们应该有相同的哈希码

2. 如果两个对象具有相同的哈希码,则它们必须相互调用equals()方法以查看它们是否相等。

3. 如果哈希码匹配,并不意味着它们都是相等的。

4. 因此,当equals()方法被重写时,hashcode()方法也必须被重写,这非常重要。

5. HashSet在将一个对象放入其中时检查该对象适合的位置,然后检查是否存在具有相同哈希码的对象,如果有,则将两个对象放入具有相同哈希码标签桶中,随后调用equals()方法相互比较以确定它们是否真正相等


0

HashSet使用HashMap作为内部数据的表示方式。HashSet.add()HashSet.remove()基于你在hashCode()方法中提供的hash值进行操作。一旦你注释掉hashCode(),所有的预测都将失效。

在这件事上,我的心有点倾向于duffymo,但另一方面,我也极力反对因为权威告诉你而去做某事,而不理解其原因。


在我看来,你没有理解这里的权威。上下文是Java集合,对于这些数据结构的正确操作建议来自设计它们的个人。 - duffymo

0

corsiKa已经回答正确了。我再补充一些要点来简化问题。 @user1582269,你正在使用Set算法,哈希是所使用的数据结构。 根据Set的概念 - 集合中的每个元素都应该是唯一的。不允许重复元素。 基于什么使元素唯一?为了实现这种唯一性,应该重写equals()方法。即Emp 1应该与Emp 2不同。 现在你正在使用哈希数据结构。哈希确保元素如何存储、检索、搜索。为了使哈希工作高效,应该重写哈希函数即hashCode(),以便不会发生冲突。

corsiKa的解释应该阅读更多关于哈希的解释。 因此,如果您不覆盖hashCode(),默认的hashCode()实现将返回某个桶ID(假设数组中的某个索引),例如4。因此,如果empX元素已经存储在桶ID 4中,并且对于另一个员工empY,返回相同的hashCode,即4,则empX和empY之间将发生冲突。

希望这解决了你的疑问。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接