【问题标题】:Java HashSet does not call overriden equals but it should [duplicate]Java HashSet 不调用覆盖的equals,但它应该[重复]
【发布时间】:2021-12-23 03:00:14
【问题描述】:

在 main 方法中,我创建了两个不同的相等对象。

private static Set<Cards> allCombs = new HashSet<>();

cards = new Cards();
cards.add(new Card(1, 10));
cards.add(new Card(1, 12));

Cards cards2 = new Cards();
cards2.add(new Card(1, 10));
cards2.add(new Card(1, 12));

System.out.println(cards.toString() + " " + cards2.toString() + " " + (cards == cards2) + " " + cards.equals(cards2));

allCombs.add(cards);
allCombs.add(cards2);

System.out.println(allCombs);

我的期望是集合 allCombs 有一个元素,因为 Cards 的两个对象是相等的。我检查了它,DEBUG 来自equals 方法并且对象相等(来自println 的比较)。但它们不是同一个参考...

为什么HashSet中的add方法在添加元素时不调用Cards的equals方法?

DEBUG 6_QT 6_QT true
6_QT 6_QT false true
[6_QT, 6_QT]

【问题讨论】:

  • 您是否也覆盖了Card 中的hashCode
  • 我看到你也有一个类Cards,所以你也需要在那里覆盖hashCode
  • 您使用的是HashSet,顾名思义,使用hashCode 来确定相等性。此外,您应该始终覆盖 equalshashCode 以保持合同一致
  • 我认为超类(对象)中的 hashCode 会相等你为什么这么认为?
  • @TomaszEkner - 用于Object::equalsObject::hashCodejavadocs 解释了这两种方法之间所需的关系。当您覆盖这些方法中的任何一个时,必须保持这种关系(也就是哈希码 等于合同)......否则您的哈希表将表现不正确。

标签: java set add equals hashset


【解决方案1】:

Hashset 使用 hashCode 方法来比较两个对象。 您必须在您的对象中覆盖此方法:卡片。

【讨论】:

  • 这是hashCode 的一个相当糟糕的实现。老实说,请让您的 IDE 生成它...
  • 您可以使用Objects.hashCode(Object... params) 为您的所有字段生成一致的hashCode。无需人工计算
  • “这只是一个例子。” - 不好的例子不好。人们复制它们。
  • @QBrute 对于多个参数,它是Objects.hash(Object... params);
  • @WJS 是的,你是对的,我的错。
【解决方案2】:

根据您描述的行为,您违反了equals()hashCode() 方法。如果您在课堂上覆盖了equals(),您还需要正确覆盖hashCode()

方法HashSet#add 使用内部映射并首先计算您的对象的hashcode,并且仅当具有计算的哈希码的篮子出现在此内部映射中时才调用equals()。您的情况显然没有这个篮子,所以这里根本不需要调用您的equals()

  1. hashCode() 必须在覆盖equals() 的每个类中被覆盖。否则,您将无法在任何基于哈希表的数据结构中正确使用您的类的实例,包括HashSet。阅读what Joshua Bloch writes about it in Effective Java

  2. hashCode() 必须根据equals() 在 2 个对象中的每一个上产生相同的整数结果。 阅读javadoc of Object class,全面了解equals()hashCode()之间的合约

  3. 请参阅 source code of HashSet class 以了解 HashSetHashMap 的工作原理以及为什么您的 equals() 方法未在您的给定代码中使用。

  4. 记得使用注解@Override,确保你真的重写了默认方法,避免方法签名中隐藏错误。

并展示您的类 CardCards 的实现,以准确地从以上几点定义问题所在。

【讨论】:

    【解决方案3】:

    从纯粹等效的角度来看,您的 Card 类应该如下所示。最重要的是使用@Override 来确保您覆盖的方法签名是正确的。

    class Card {
           int suit;
           int rank;
           public Card (int suit, int rank) {
              this.suit = suit;
              this.rank = rank;
           }
           @Override
           public int hashCode() {
               return Objects.hash(suit, rank);
           }
           @Override
           public boolean equals(Object obj) {
               if (obj == this) {
                    return true;
               }
               if (obj == null) {
                  return false;
               }
               if (obj instanceof Card) {
                    Card arg = (Card) obj;
                    if (arg.suit == suit && arg.rank == rank) {
                        return true;
                    }
                }
                return false;
           }
           // other methods here
    }           
    

    【讨论】:

    • 正确。但我敢打赌suitrank 是引用类型Integer
    猜你喜欢
    • 2014-01-03
    • 1970-01-01
    • 2011-04-24
    • 2017-11-19
    • 1970-01-01
    • 2016-05-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多