views:

128

answers:

4

Hey,

I've come across a really strange problem. I have written a simple Deck class which represents a standard 52 card deck of playing cards. The class has a method missingCards() which returns the set of all cards which have been drawn from the deck. If I try and compare two identical sets of missing cards using .equals() I'm told they are different, and if I check to see if a set contains an element that I know is there using .contains() I am returned false.

Here is my test code:

    public void testMissingCards() 
{
    Deck deck = new Deck(true);
    Set<Card> drawnCards = new HashSet<Card>();
    drawnCards.add(deck.draw());
    drawnCards.add(deck.draw());
    drawnCards.add(deck.draw());
    Set<Card> missingCards = deck.missingCards();
    System.out.println(drawnCards);
    System.out.println(missingCards);
    Card c1 = null;
    for (Card c : drawnCards){
        c1 = c;
    }
    System.out.println("C1 is "+c1);
    for (Card c : missingCards){
        System.out.println("C is "+c);
        System.out.println("Does c1.equal(c) "+c1.equals(c));
        System.out.println("Does c.equal(c1) "+c.equals(c1));
    }
    System.out.println("Is c1 in missingCards "+missingCards.contains(c1));
    assertEquals("Deck confirm missing cards",drawnCards,missingCards);
}

(Edit: Just for clarity I added the two loops after I noticed the test failing. The first loop pulls out a card from drawnCards and then this card is checked against every card in missingCards - it always matches one, so that card must be contained in missingCards. However, missingCards.contains() fails)

And here is an example of it's output:

[5C, 2C, 2H]
[2C, 5C, 2H]
C1 is 2H
C is 2C
Does c1.equal(c) false
Does c.equal(c1) false
C is 5C
Does c1.equal(c) false
Does c.equal(c1) false
C is 2H
Does c1.equal(c) true
Does c.equal(c1) true
Is c1 in missingCards false

I am completely sure that the implementation of .equals on my card class is correct and, as you can see from the output it does work!

What is going on here?

Cheers,

Pete

+9  A: 

You forgot to implement hashCode() in a way consistent to equals() :) (I.e., equal objects must return the same hash code).

Dimitris Andreou
Almost certainly true. Poster should really have posted Card.java for us to be sure.
Kevin Bourrillion
Thanks for the answer, I hadn't implemented hashCode(). Do you have any tips on how I can best implemented hashCode (a card has a the fields face and suit which are both integers)?
Peter
Eclipse will auto generate a reasonable hashCode and equals at the same time in 1 click.
bwawok
@Peter, Effective Java 2nd version, Item 9, contains a whole load of tips. Some shortcuts include Arrays.hashCode() (http://java.sun.com/javase/6/docs/api/java/util/Arrays.html?is-external=true#hashCode(java.lang.Object[])) and Objects.hashCode() (of Guava) (http://guava-libraries.googlecode.com/svn/trunk/javadoc/com/google/common/base/Objects.html#hashCode(java.lang.Object...))Hmm, am I missing a better way to paste links in comments?
Dimitris Andreou
+1  A: 

What about hashCode? You should always override the two together, according to Joshua Bloch's "Effective Java" chapter 3.

Post your Card class. It'll be easy to spot then.

duffymo
Why on earth was this downvoted? It's the same answer as the one that was accepted.
duffymo
+1 The squeaky wheel gets the up vote!
DutrowLLC
+1  A: 

Please post the code for the Deck class -- at least the equals(Object) and the hashCode() methods.

My first guess is that you only implemented equals(), but not hashCode(). If that's the case, read the documentation of the java.lang.Object class.

Roland Illig
+3  A: 

Java - Is Set.contains() broken on OpenJDK 6?

No it is not.

The first rule of debugging Java is that 99.9% of the time it is your code that is broken, and not the Java standard libraries.

Stephen C