有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

java如何确保hashCode()与equals()一致?

重写java的equals()函数时。对象,javadocs建议

it is generally necessary to override the hashCode method whenever this method is overridden, so as to maintain the general contract for the hashCode method, which states that equal objects must have equal hash codes.

hashCode()方法必须为每个对象返回一个唯一整数(这在基于内存位置比较对象时很容易做到,只需返回对象的唯一整数地址即可)

如何重写hashCode()方法,使其仅基于每个对象的属性为该对象返回一个唯一的整数


public class People{
   public String name;
   public int age;

   public int hashCode(){
      // How to get a unique integer based on name and age?
   }
}
/*******************************/
public class App{
   public static void main( String args[] ){
       People mike = new People();
       People melissa = new People();
       mike.name = "mike";
       mike.age = 23;
       melissa.name = "melissa";
       melissa.age = 24;
       System.out.println( mike.hasCode() );  // output?
       System.out.println( melissa.hashCode(); // output?
   }
}

共 (6) 个答案

  1. # 1 楼答案

    hashCode的唯一合同义务是保持一致性。创建hashCode值时使用的字段必须与equals方法中使用的字段相同或是其子集。这意味着为所有值返回0是有效的,尽管不是有效的

    可以通过单元测试检查hashCode是否一致。我编写了一个名为EqualityTestCase的抽象类,它执行一些哈希代码检查。只需扩展测试用例并实现两个或三个工厂方法。测试做了一项非常粗糙的工作,测试哈希代码是否有效

  2. # 2 楼答案

    我将不深入讨论hashCode唯一性的细节,因为Marc已经解决了这个问题。对于你的People类,你首先需要决定一个人的平等意味着什么。也许平等仅仅是基于他们的名字,也许是基于名字和年龄。它将是特定于域的。假设平等是基于姓名和年龄的。您被重写的equals如下所示

    public boolean equals(Object obj) {
        if (this==obj) return true;
        if (obj==null) return false;
        if (!(getClass().equals(obj.getClass())) return false;
        Person other = (Person)obj;
        return (name==null ? other.name==null : name.equals(other.name)) &&
            age==other.age;
    }
    

    任何时候重写equals都必须重写hashCode。此外,hashCode在计算中不能使用比equals更多的字段。大多数情况下,您必须添加或排除各个字段的哈希代码(哈希代码的计算速度应该很快)。因此,有效的hashCode方法可能如下所示:

    public int hashCode() {
        return (name==null ? 17 : name.hashCode()) ^ age;
    }
    

    请注意,以下无效,因为它使用了equals没有(高度)的字段。在这种情况下,两个“equals”对象可能具有不同的哈希代码

    public int hashCode() {
        return (name==null ? 17 : name.hashCode()) ^ age ^ height;
    }
    

    另外,两个不相等的对象具有相同的哈希代码也是完全有效的:

    public int hashCode() {    
        return age;    
    }
    

    在本例中,Jane 30岁不等于Bob 30岁,但他们的哈希代码都是30。虽然有效,但这对于基于哈希的集合的性能是不可取的

  3. # 3 楼答案

    另一个问题是,是否所有程序员都应该知道一些基本的低级知识,我认为哈希查找就是其中之一。这就来了

    哈希表(请注意,我没有使用实际的类名)基本上是一个链表数组。要在表中查找某个内容,首先计算该内容的哈希代码,然后根据表的大小对其进行修改。这是数组中的一个索引,在该索引处可以得到一个链表。然后遍历列表,直到找到对象

    由于数组检索是O(1),而链表遍历是O(n),因此需要一个哈希函数,该函数创建尽可能随机的分布,以便将对象哈希到不同的列表。每个对象都可以返回值0作为其哈希代码,哈希表仍然可以工作,但它本质上是数组元素0处的一个长链表

    您通常还希望数组较大,这会增加对象位于长度为1的列表中的可能性。例如,当映射中的条目数为>;时,Java HashMap会增加数组的大小;阵列大小的75%。这里有一个折衷办法:您可以使用一个条目很少且浪费内存的巨大数组,或者使用一个较小的数组,其中数组中的每个元素都是一个带有>;1个条目,浪费时间遍历。一个完美的散列将把每个对象分配到数组中一个唯一的位置,而不会浪费空间

    术语“完美散列”是一个实数术语,在某些情况下,您可以创建一个散列函数,为每个对象提供一个唯一的数字。这只有在您知道所有可能值的集合时才可能。在一般情况下,您无法实现这一点,并且会有一些值返回相同的哈希代码。这是一个简单的数学问题:如果字符串长度超过4字节,则无法创建唯一的4字节哈希代码

    有一个有趣的小贴士:哈希数组的大小通常基于素数,以便在修改结果时提供随机分配的最佳机会,而不管哈希代码实际上有多随机

    根据评论进行编辑:

    1)链表不是表示具有相同哈希代码的对象的唯一方法,尽管这是JDK 1.5 HashMap使用的方法。虽然比简单数组的内存效率低,但可以说它在重新灰化时确实会产生更少的混乱(因为条目可以从一个bucket中取消链接,然后重新链接到另一个bucket)

    2)从JDK1.4开始,HashMap类使用的数组大小为2的幂;在此之前,它使用了2^N+1,我认为这是N<;=正如Neil Coffey所指出的那样,这并不能加快数组索引本身的速度,但允许使用位AND而不是除法来计算数组索引。就我个人而言,我怀疑这是过早的优化,但考虑到HashMap上的作者列表,我会认为这有一些真正的好处

  4. # 4 楼答案

    它并没有说一个对象的hashcode必须是完全唯一的,只是说两个相等对象的hashcode返回相同的hashcode。让两个不相等的对象返回相同的哈希代码是完全合法的。但是,哈希代码分布在一组对象上越独特,使用哈希代码的哈希映射和其他操作的性能就越好

    IDE(如IntelliJ Idea)具有内置的equals和hashCode生成器,通常能够很好地为大多数对象生成“足够好”的代码(可能比一些手工制作的过于聪明的哈希函数更好)

    例如,下面是Idea为您的People类生成的hashCode函数:

    public int hashCode() {
        int result = name != null ? name.hashCode() : 0;
        result = 31 * result + age;
        return result;
    }
    
  5. # 5 楼答案

    通常,哈希代码不能是唯一的,因为其值多于可能的哈希代码(整数)。 一个好的散列码将值很好地分布在整数上。 坏的哈希表总是可以给出相同的值,并且逻辑上仍然正确,这只会导致无法接受的低效率哈希表

    相等的值必须具有相同的哈希值,哈希表才能正常工作。 否则,您可以向哈希表中添加一个键,然后尝试使用不同的哈希代码通过一个相等的值来查找它,但找不到它。 或者您可以使用不同的哈希代码放置一个相等的值,并且在哈希表中的不同位置有两个相等的值

    在实践中,通常在hashCode()和equals()方法中选择要考虑的字段子集

  6. # 6 楼答案

    我想你误解了。哈希代码不必对每个对象都是唯一的(毕竟,它是一个哈希代码),尽管您显然不希望它对所有对象都是相同的。但是,您确实需要它与所有相等的对象相同,否则像标准集合这样的东西将不起作用(例如,您将在哈希集中查找某些东西,但找不到它)

    对于简单的属性,一些IDE有hashcode函数生成器

    如果不使用IDE,请考虑使用ApHACE公信包和类HASCODEBuudio