15b: Equality

ICS211, Fall 2012
Dr. Zach

(switch view)

Status check

Exam 3 Results

Equality and Immutability

PlayingCard (from last time)

public class PlayingCard {

  private int value;
  private Suit suit;

  public PlayingCard(int value, Suit suit) {
    if (value < 1 || value > 13) {
      throw new IllegalArgumentException("Value " + value +
          " out of valid 1 - 13 range.");
    }
    this.value = value;
    this.suit = suit;
  }

  public int getValue() {
    return this.value;
  }
  public Suit getSuit()  {
    return this.suit;
  }

  @Override
  public String toString() {
    String v = "" + this.value;
    switch (this.value) {
      case 1:  v = "A"; break;
      case 11: v = "J"; break;
      case 12: v = "Q"; break;
      case 13: v = "K"; break;
    }
    return v + this.suit;
  }

  // nested class for Suit
  public static enum Suit {
    C, D, H, S;
  }
}

At this point

  PlayingCard fourC = new PlayingCard(4, PlayingCard.Suit.C);
  PlayingCard fourC2 = new PlayingCard(4, PlayingCard.Suit.C);
  PlayingCard jackH = new PlayingCard(11, PlayingCard.Suit.H);

  // overridden toString() (vs orginal)
  System.out.println(fourC);  // 4C  (was: PlayingCard@4d29dcc0)
  System.out.println(fourC2); // 4C  (was: PlayingCard@4c53ccba)
  System.out.println(jackH);  // JH  (was: PlayingCard@11a5ee7c)

  // equals
  System.out.println(fourC.equals(fourC));  // true
  System.out.println(fourC.equals(jackH));  // false
  System.out.println(fourC.equals(fourC2)); // false
  
  // as Set
  List<PlayingCard> hand = Arrays.asList(fourC, fourC2, jackH);
  Set<PlayingCard> set = new HashSet<PlayingCard>(hand);
  System.out.println(set);  // [4C, JH, 4C]

Equals method

PlayingCard equals

  @Override
  public boolean equals(Object obj) {
    if (obj instanceof PlayingCard) {
      PlayingCard pc = (PlayingCard) obj;
      return this.getValue() == pc.getValue() && this.getSuit() == pc.getSuit();
    }
    return false;
  }

Effect of new equals

  PlayingCard fourC = new PlayingCard(4, PlayingCard.Suit.C);
  PlayingCard fourC2 = new PlayingCard(4, PlayingCard.Suit.C);
  PlayingCard jackH = new PlayingCard(11, PlayingCard.Suit.H);

  // equals
  System.out.println(fourC.equals(fourC));  // true
  System.out.println(fourC.equals(jackH));  // false
  System.out.println(fourC.equals(fourC2)); // true
  
  // as set
  List<PlayingCard> hand = Arrays.asList(fourC, fourC2, jackH);
  Set<PlayingCard> set = new HashSet<PlayingCard>(hand);
  System.out.println(set);  // [4C, JH, 4C]  still the same?
  System.out.println(set.contains(new PlayingCard(4, PlayingCard.Suit.C)); //false !?!

More complex equals example

Second attempt

  //In Point...
  @Override
  public boolean equals(Object obj) {
    if (obj instanceof Point) {
      Point p = (Point) obj;
      return this.x == p.x && this.y == p.y;
    }
    return false;
  }
  //In Pixel...
  @Override
  public boolean equals(Object obj) {
    if (obj instanceof Point) {
      if (obj instanceof Pixel) {
        Pixel p = (Pixel) obj;
        return this.x == p.x && this.y == p.y && this.color == p.color;
      }else {
        //just a Point, so ignore color
        return super.equals(obj);
      }
    }
    return false;
  }

Returning to PlayingCard

  // hashCode
  System.out.printf("%x\n", fourC.hashCode());  // 4d29dcc0
  System.out.printf("%x\n", fourC2.hashCode()); // 4c53ccba
  System.out.printf("%x\n", jackH.hashCode());  // 11a5ee7c

hashCode

Writing hashCode

PlayingCard's hashCode

@Override
public int hashCode() {
  int total = 17;
  total = total * 31 + this.value;
  total = total * 31 + this.suit.hashCode();   //or .ordinal() for enums
  return total;
}

Effect of new hashCode()

  // hashCode
  System.out.printf("%x\n", fourC.hashCode());  // 3312f22a (404d with .ordinal())
  System.out.printf("%x\n", fourC2.hashCode()); // 3312f22a (404d with .ordinal())
  System.out.printf("%x\n", jackH.hashCode());  // 24cc5917 (4128 with .ordinal())
  
  // as set
  List<PlayingCard> hand = Arrays.asList(fourC, fourC2, jackH);
  Set<PlayingCard> set = new HashSet<PlayingCard>(hand);
  System.out.println(set);  // [4C, JH] 
  System.out.println(set.contains(new PlayingCard(4, PlayingCard.Suit.C)); // true

Comparable

Making PlayingCard Comparable

public class PlayingCard implements Comparable<PlayingCard> {

  //...

  /**
   * Natural ordering is by value only (smaller to larger).
   * This is inconsistent with equals.
   */
  @Override
  public int compareTo(PlayingCard pc) {
    return this.value - pc.value;
  }

Potential bugs

  // as a Set
  PlayingCard fourD = new PlayingCard(4, PlayingCard.Suit.D);
  List<PlayingCard> hand = Arrays.asList(fourC, fourD, jackH);
  Set<PlayingCard> set = new HashSet<PlayingCard>(hand);
  System.out.println(set);   // [4C, JH, 4D]
  System.out.println(set.contains(new PlayingCard(4, PlayingCard.Suit.S)));  // false
  // as a Set
  PlayingCard fourD = new PlayingCard(4, PlayingCard.Suit.D);
  List<PlayingCard> hand = Arrays.asList(fourC, fourD, jackH);
  Set<PlayingCard> set = new TreeSet<PlayingCard>(hand);
  System.out.println(set);   // [4C, JH] (but no 4D)
  System.out.println(set.contains(new PlayingCard(4, PlayingCard.Suit.S)));  // true

For Next Time