如果仅在类中重写hashCode()并在Set中使用它,会发生什么情况?


问题内容

这可能不是现实情况,只是好奇地知道会发生什么,下面是代码。

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

由于我尚未重写equals方法,因此默认实现将返回false。那么,将具有哈希码97的先前对象保留在同一个存储桶中,将值“
a”的对象保留在何处?还是会创建新的存储桶?有人知道如何将其内部存储吗?

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

问题答案:

James Large答案是错误的,或者是误导性的(部分错误也是如此)。我会解释。

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

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

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

的确,如果两个对象没有相同的哈希,那么它们将不相等。但是,哈希不是检查相等性的方法-因此说它是检查相等性的较快方法是非常不正确的。

同样,说hashCode函数是执行任何操作的有效方法,这也是非常不正确的。这完全取决于实现,但是随着String变大,字符串的hashCode的默认实现效率很低。它将基于String的每个字符执行计算,因此,如果您使用大型Strings作为键,则效率会非常低;此外,如果您有大量的存储桶。

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

不包含重复元素的集合。

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

通常来说,优良作法是,如果覆盖hashCode函数,那么也将覆盖equals函数(如果我没记错的话,如果选择不这样做,就会违反合同)。