java LinkedHashSet

6

我一直在学习OCJP(以前的SCJP),我遇到了以下使用LinkedHashSet的示例:

public class Test{

    int size;

    public Test(int s){
       this.size = s;
    }

    @Override
    public boolean equals(Object obj) {
         return (this.size == ((Test)obj).size);
    }

    public static void main(String[] args) {
      LinkedHashSet<Test> s = new LinkedHashSet<Test>();
      s.add(new Test(1));
      s.add(new Test(2));
      s.add(new Test(1));
      System.out.println(s.size());
    }
}

现在的问题是,如果:
1)实现保持不变
2)将hashCode的覆盖插入到Test类中,那么会显示什么:

public int hashCode() {return size/5};

运行和编译代码后发现,在第一种情况下,集合的大小是3,而在第二种情况下,集合的大小是2。 为什么?
在第一个案例中,尽管equals方法被覆盖,但它从未被调用。这是否意味着如果未覆盖hashCode方法,add()方法将不检查对象相等性? 在第二个案例中,给定实现和测试对象集的hashCode始终返回相同的数字。这与默认的hashCode实现有何不同,并为什么这会导致调用equals方法?
2个回答

10
如果您不重写hashCode(),那么每个实例的哈希码将从Object类中的某个预定义哈希算法计算而来。因此,所有实例可能都具有不同的哈希码值(尽管这并不确定)。这意味着,每个实例将进入其自己的
现在,即使您覆盖了equals()方法,使两个实例基于某个属性相等,它们的哈希码仍然不同。
因此,具有不同哈希码的两个实例永远不可能相等。因此,这个集合的大小是3。因为它没有任何重复项。

但是,当您使用以下实现覆盖hashCode()时:

public int hashCode() {return size/5};

它将针对相同的size返回相同的值。因此,具有相同size值的实例将具有相同的哈希码,并且由于您已经在equals方法中基于size进行比较,因此它们将是equal的,因此它们将被视为重复项在您的Set中并因此将被删除。因此,Set.size()为2。 道德: - 每当您覆盖equals()方法时,应始终覆盖hashCode(),以保持两种方法之间的一般契约。 hashcodeequals方法之间的一般契约: -
  • 当两个对象相等时,它们的哈希码必须相等
  • 当两个对象不相等时,它们的哈希码可以相等
  • hashCode算法应始终为同一对象生成相同的值。
  • 如果两个对象的hashCode不同,则它们不相等
  • 始终使用相同的属性计算hashCode,这些属性用于比较两个实例

强烈建议至少阅读一次:-


简单来说,当使用默认的hashCode实现时,每个对象都在自己的“桶”中。由于桶中只有一个对象,所以没有什么可以与之比较,因此它只是被添加到集合中?我想我开始明白了。 - Maggie
@Maggie。我建议你阅读我给出的最后一个链接。那个链接提供了关于这个主题的最好的解释。 - Rohit Jain
1
是的,我也有那本书,而且我想我之前读过它,但这个集合/泛型主题让我头痛不已。无论如何,还是谢谢你。 - Maggie
@Maggie。不用谢。别让那本书在某个黑暗的房间里丢失了。它值得读十遍。 - Rohit Jain

2
哈希结构依赖于哈希算法,它在 Java 中由 hashCode() 表示。当您将某些东西放入 HashMap(或您的情况下是 LinkedHashSet)中时,JVM 会在插入到此结构中的对象上调用 hashCode()。当未覆盖默认的 hashCode()(来自 Object 类)时,效率非常低下,所有对象都进入自己的桶中。
当您以您的示例中所示的方式重写 hashCode() 时,您的示例中的所有对象都将进入同一个桶中。然后(当一个接一个地添加它们时),将使用 equals() 进行比较。这就是为什么在第一种情况下(不调用equals()),您获得了大小为3,在第二种情况下获得了2的原因。

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