1

In main method I create two different equal objects.

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);

My expectations is that set allCombs has one element because two objects of Cards are equal. I checked it and DEBUG is from equals method and objects are equal (comparison from println). But they are not same reference...

Why add method from HashSet does not call Cards's equals method while adding element?

DEBUG 6_QT 6_QT true
6_QT 6_QT false true
[6_QT, 6_QT]
Nowhere Man
  • 19,170
  • 9
  • 17
  • 42
  • 6
    Did you override `hashCode` in `Card` as well? – QBrute Nov 10 '21 at 13:56
  • I've seen that you also have a class `Cards`, so you'd need to override `hashCode` there as well – QBrute Nov 10 '21 at 14:04
  • Why I need hashCode in Cards? I thought that hashCode from super class (Object) will be equal and then Set's add method compares by equals – Tomasz Ekner Nov 10 '21 at 14:10
  • 2
    You're using a `HashSet`, and as the name suggests uses the `hashCode` to determine equalness. Also, you should always override both `equals` and `hashCode` to keep the contract consistent – QBrute Nov 10 '21 at 14:13
  • 1
    _I thought that hashCode from super class (Object) will be equal_ Why did you think this? – Sotirios Delimanolis Nov 10 '21 at 14:56
  • 1
    @TomaszEkner - The [javadocs](https://docs.oracle.com/javase/7/docs/api/java/lang/Object.html#hashCode()) for `Object::equals` and `Object::hashCode` explain the relationship that is required between these two methods. When you override either of these methods, that relationship (aka the hashcode <-> equals contract) must be maintained ... or else your hash tables will behave incorrectly. – Stephen C Nov 10 '21 at 15:06

3 Answers3

2

From a purely equivalence perspective, your Card class should look something like this. It's most important to use @Override to ensure your overridden method signatures are correct.

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
}           
WJS
  • 36,363
  • 4
  • 24
  • 39
1

The Hashset use hashCode method to compare two objects. You must override this method in your object: Cards.

1

You have contract violation of equals() and hashCode() methods based on the behavior you describe. If you have overridden equals() in your class you need to properly override hashCode() as well.

Method HashSet#add uses an internal map and first calculates hashcode of your object and invoke equals() ONLY if a basket with the calculated hashcode is present in this internal map. This basket is obviously absent in your case so no need invoke your equals() at all here.

  1. hashCode() must be overrided in every class that overrides equals(). Otherwise you'll not be able to properly use instances of your class in any data structures based on hash tables, including HashSet. Read what Joshua Bloch writes about it in Effective Java

  2. hashCode() must produce the same integer result on each of 2 objects are equal according to equals(). Read javadoc of Object class to fully understand the contract between equals() and hashCode()

  3. See source code of HashSet class to understand how HashSet and HashMap work and why your equals() method is not used in your given code.

  4. Remember to use annotation @Override to make sure you really override the default method and avoid hidden mistakes in the method signature.

And show the implementation of your classes Card and Cards to define what's wrong exactly from the points above.

kapandron
  • 3,546
  • 2
  • 25
  • 38