11

所以我对 hashcode() 和 equals() 方法有疑问

假设我只是编写了一个覆盖这两种方法的非常基本的程序

import java.util.*;

class Employee
{
   private String name;
   private int empid;


   public Employee(String name,int empid)
   {
       this.name=name;
       this.empid=empid;
   }


   public int getEmpid()
   {
       return empid;
   }


   public String getName()
   {
       return name;
   }


   public boolean equals(Object obj)
   {
       System.out.println("equals has just been called...");
       Employee e1=(Employee)obj;
       return ((name.equals(e1.name)) && (empid==e1.empid));
   }


   public int hashCode()
   {
       System.out.println("hashcode called...");
       return empid;
   }

}

然后,假设我编写了另一个类来添加和迭代 HashSet 中的元素

class Five
{
   public static void main(String args[])
   {
       HashSet hs1=new HashSet();
       hs1.add(new Employee("Alex",25));
       hs1.add(new Employee("Peter",25));
       hs1.add(new Employee("Martin",25));
       hs1.add(new Employee("Alex",25));


       Iterator itr=hs1.iterator();

       while(itr.hasNext())
       {
           Employee e=(Employee)itr.next();
           System.out.println(e.getEmpid()+"\t"+e.getName());
       }


    }

}

现在的问题是,当我尝试使用相同的 empid 再次添加 Alex 时,equals() 总是调用你的时间

因为没有索引 n 哈希图,所以如果它首先与之前添加的 Alex 进行检查,它将返回 true 并且不应该为其他两个元素(peter 和 martin)调用但等于总是调用 3 次

为什么..??

同一个桶中的对象是否也有索引..??

4

4 回答 4

14

Equals在添加和删除元素时,总是hashCode在 java 散列集合中的方法之后调用。原因是,如果指定的存储桶中已经有一个元素,那么 JVM 会检查它是否与它试图放入的元素相同。如果equals返回false,则元素被添加到同一个桶中,但在桶的列表末尾。所以现在你只是在同一个桶中没有一个元素,而是一个元素列表。

现在在检索元素时,将调用第一个 hashCode 以到达所需的存储桶,然后使用 equals 扫描列表以获取所需的元素。

的理想实现hashCode将确保每个桶的列表大小为 1。因此元素的检索使用 O(1) 复杂度完成。但是如果有多个元素存储在一个桶的列表中,那么元素的检索将通过 O(n) 复杂度来完成,其中 n 是列表的大小。

顺便说一句,在 HashSet 的情况下,桶中没有创建列表,而是在 hashcode 和 equals 相同时简单地替换对象。ist 创建行为在 hashmap 中。

于 2013-07-29T08:45:19.693 回答
3

Ajava.util.HashSet使用 ajava.util.HashMap作为其存储。Ajava.util.HashMap使用链接Entry对象来表示地图中的桶。如果您按照源代码进行操作,您将获得以下构造函数java.util.HashMap.Entry

Entry(int h, K k, V v, Entry<K,V> n) 
{
  value = v;
  next = n;
  key = k;
  hash = h;
}

从中您可以看到新项目被添加到存储桶的开头(Entry n代表存储桶的第一个Entry),因此在您的情况下,存储桶中的项目(只有一个存储桶,因为每个存储桶的哈希码Employee是相同的) 将按以下顺序排列:

Martin -> Peter -> Alex

因此,当第二次添加 Alex 时,每个值都会在到达 Alex 之前检查是否相等。

于 2013-07-29T09:01:21.857 回答
3

在插入期间,HashSet第一次调用hashCode并查看新值属于哪个存储桶。它看到已经有三个条目(都带有hashCode() 25)。

所以它然后通过使用进行比较equals()。而且因为有 3 个条目,它必须检查所有导致调用equals()3 次的条目。

于 2013-07-29T08:48:33.177 回答
0

具有相同散列的多个对象存储为LinkedList,并在 处添加新元素HEAD。因此,在您的情况下,由于所有哈希值都相同,因此 LinkedList 的顺序如下:

马丁->彼得->亚历克斯。

当您添加另一个“Alex”时,将从 HEAD 遍历列表。

去测试:

public boolean equals(Object obj)
   {
       Employee e1=(Employee)obj;
       System.out.println(this.name +  "'s equals has just been called against " + e1.name );
       return ((name.equals(e1.name)) && (empid==e1.empid));
   }
于 2013-07-29T08:46:38.927 回答