groovy
  1. groovy
  2. GROOVY-4335

Set doesn't consider equals() method

    Details

    • Type: Bug Bug
    • Status: Closed Closed
    • Priority: Major Major
    • Resolution: Not A Bug
    • Affects Version/s: 1.7.4
    • Fix Version/s: 1.7.4
    • Component/s: None
    • Labels:
      None
    • Number of attachments :
      0

      Description

      //  the Card class has equals() method implemented, so this works:
      assert new Card("6s") == new Card("6s")
      
      //  here's a list with 3 equal cards:
      def list = ["4c", "6s", "6s", "6s", "6c"].collect {new Card(it)}
      
      //  since three cards are equal, the following is pretty much expected:
      assert list.clone().unique().size() == 3
      
      //  but Set doesn't recognize cards as equal:
      assert (list as Set).size() == 5
      

      Since Set's definition states that it's an unordered collection of unique items, and all other statements prove that three items of this collection are not unique, I believe it's a bug.

        Activity

        Hide
        Paul King added a comment -

        What does your hashCode() method look like?

        If I supply one manually or using Groovy 1.8's helper transforms, then it works as expected:

        @groovy.transform.Canonical
        class Card {
          final rank, suit
          Card(String s) { (rank, suit) = s }
        }
        

        or

        @groovy.transform.EqualsAndHashCode
        class Card {
          final rank, suit
          Card(String s) { (rank, suit) = s }
        }
        

        or

        class Card {
          final rank, suit
          Card(String s) { (rank, suit) = s }
          boolean equals(other) { rank == other.rank && suit == other.suit }
          int hashCode() { rank.hashCode() + 31 * suit.hashCode() }
        }
        

        These all then give this result:

        assert (list as Set).size() == 3
        
        Show
        Paul King added a comment - What does your hashCode() method look like? If I supply one manually or using Groovy 1.8's helper transforms, then it works as expected: @groovy.transform.Canonical class Card { final rank, suit Card( String s) { (rank, suit) = s } } or @groovy.transform.EqualsAndHashCode class Card { final rank, suit Card( String s) { (rank, suit) = s } } or class Card { final rank, suit Card( String s) { (rank, suit) = s } boolean equals(other) { rank == other.rank && suit == other.suit } int hashCode() { rank.hashCode() + 31 * suit.hashCode() } } These all then give this result: assert (list as Set).size() == 3
        Hide
        Nikita Y Volkov added a comment -

        Oh, craps! Didn't consider hashCode at all. Since I'm on 1.7.4 third way worked for me. Thanks for the help and sorry for false alarm!
        One question: why did you use 31 as multiplier for suit.hashCode() - or did you mean 13?

        Show
        Nikita Y Volkov added a comment - Oh, craps! Didn't consider hashCode at all. Since I'm on 1.7.4 third way worked for me. Thanks for the help and sorry for false alarm! One question: why did you use 31 as multiplier for suit.hashCode() - or did you mean 13?
        Hide
        Nikita Y Volkov added a comment -

        Implementation of hashCode() solves the problem

        Show
        Nikita Y Volkov added a comment - Implementation of hashCode() solves the problem
        Hide
        Paul King added a comment -

        The hashCode/equals contract only requires that equal objects return the same hashcode. However hashes and hashSets perform better when non-equal objects return different hashCode values. A common algorithm that works well in generating such distinct hashCode values for a composite object is one of the hashCodes added to some prime multiplied by the hashCode of other elements. The actual value 31 isn't so important, 13 could have been used too - it is prime and also the number of possible rank values - so for either reason would produce a suitable hashCode.

        Show
        Paul King added a comment - The hashCode/equals contract only requires that equal objects return the same hashcode. However hashes and hashSets perform better when non-equal objects return different hashCode values. A common algorithm that works well in generating such distinct hashCode values for a composite object is one of the hashCodes added to some prime multiplied by the hashCode of other elements. The actual value 31 isn't so important, 13 could have been used too - it is prime and also the number of possible rank values - so for either reason would produce a suitable hashCode.

          People

          • Assignee:
            Unassigned
            Reporter:
            Nikita Y Volkov
          • Votes:
            0 Vote for this issue
            Watchers:
            1 Start watching this issue

            Dates

            • Created:
              Updated:
              Resolved: