equals, again
Module 23 gave classes an equals of their own, so that two objects with the same values count as equal. Module 31 used Strings and Integers as the keys of hash maps. This module brings the two together: objects of your own classes as hash map keys. That takes one more method, hashCode, and this step shows why.
A quick recap
Here's the theater seat from module 23, with its equals:
import java.util.ArrayList;
public class Main {
public static void main(String[] args) {
Seat mine = new Seat("B", 7);
Seat yours = new Seat("B", 7);
System.out.println(mine == yours);
System.out.println(mine.equals(yours));
ArrayList<Seat> booked = new ArrayList<>();
booked.add(mine);
booked.add(new Seat("C", 2));
System.out.println(booked.contains(new Seat("C", 2)));
System.out.println(booked.indexOf(new Seat("B", 7)));
booked.remove(new Seat("B", 7));
System.out.println(booked);
}
}public class Seat {
private String row;
private int number;
public Seat(String row, int number) {
this.row = row;
this.number = number;
}
public boolean equals(Object compared) {
if (this == compared) {
return true;
}
if (!(compared instanceof Seat)) {
return false;
}
Seat other = (Seat) compared;
return this.row.equals(other.row) && this.number == other.number;
}
public String toString() {
return this.row + this.number;
}
}false
true
true
0
[C2]==on two objects asks whether both variables refer to the same object.mineandyourscome from twonews, so they're two objects, and==saysfalseeven though their values are the same.equalsis Seat's own method, written the classic way: the headerpublic boolean equals(Object compared),truefor the very same object,falsefor anything that isn't a Seat (instanceof), and otherwise a cast to Seat and a comparison of the values. The row is a String, so it's compared withequals, and the number with==.- A list's
contains,indexOfandremove(value)compare withequals, so they find a brand-new Seat that's equal to one in the list.
Seats as hash map keys
A theater wants to look up who holds each seat, so it keeps a HashMap<Seat, String> from a seat to a name. It's the same Seat class, with the same equals:
import java.util.HashMap;
public class Main {
public static void main(String[] args) {
HashMap<Seat, String> holders = new HashMap<>();
Seat seat = new Seat("B", 7);
holders.put(seat, "Ada");
System.out.println(holders.get(seat));
System.out.println(holders.get(new Seat("B", 7)));
System.out.println(holders.containsKey(new Seat("B", 7)));
holders.put(new Seat("B", 7), "Ben");
System.out.println(holders.size());
}
}public class Seat {
private String row;
private int number;
public Seat(String row, int number) {
this.row = row;
this.number = number;
}
public boolean equals(Object compared) {
if (this == compared) {
return true;
}
if (!(compared instanceof Seat)) {
return false;
}
Seat other = (Seat) compared;
return this.row.equals(other.row) && this.number == other.number;
}
public String toString() {
return this.row + this.number;
}
}Ada
null
false
2- With the very object that was put in,
getfinds Ada. new Seat("B", 7)is equal to that key, and a list would have found it. The map doesn't:getgivesnull, andcontainsKeygivesfalse.- It gets worse. A
putwith a key that's already in the map should replace its value (module 31), but the new B7 seat didn't replace Ada: the map added it as a second key. Now it holds B7 twice, once for Ada and once for Ben.
A hash map doesn't go through all of its keys and compare each one with equals, the way a list's contains does: that would be slow in a big map. It first asks the key for a number, its hash code, and uses that number to decide where to look. Seat has an equals, but no hashCode of its own, so in practice the map looks in the wrong place. The next step shows what a hash code is, and step 3 gives Seat a hashCode. Until then, keep one rule in mind: a class whose objects are hash map keys needs both methods.