{"id":"equals-hashcode-equals-again","slug":"equals-again","title":"equals, again","text":"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.\n\n### A quick recap\n\nHere's the theater seat from module 23, with its `equals`:\n\n```java\nimport java.util.ArrayList;\n\npublic class Main {\n    public static void main(String[] args) {\n        Seat mine = new Seat(\"B\", 7);\n        Seat yours = new Seat(\"B\", 7);\n        System.out.println(mine == yours);\n        System.out.println(mine.equals(yours));\n\n        ArrayList<Seat> booked = new ArrayList<>();\n        booked.add(mine);\n        booked.add(new Seat(\"C\", 2));\n        System.out.println(booked.contains(new Seat(\"C\", 2)));\n        System.out.println(booked.indexOf(new Seat(\"B\", 7)));\n        booked.remove(new Seat(\"B\", 7));\n        System.out.println(booked);\n    }\n}\n// ==== Seat.java ====\npublic class Seat {\n    private String row;\n    private int number;\n\n    public Seat(String row, int number) {\n        this.row = row;\n        this.number = number;\n    }\n\n    public boolean equals(Object compared) {\n        if (this == compared) {\n            return true;\n        }\n        if (!(compared instanceof Seat)) {\n            return false;\n        }\n        Seat other = (Seat) compared;\n        return this.row.equals(other.row) && this.number == other.number;\n    }\n\n    public String toString() {\n        return this.row + this.number;\n    }\n}\n```\n\n```output\nfalse\ntrue\ntrue\n0\n[C2]\n```\n\n- `==` on two objects asks whether both variables refer to the same object. `mine` and `yours` come from two `new`s, so they're two objects, and `==` says `false` even though their values are the same.\n- `equals` is Seat's own method, written the classic way: the header `public boolean equals(Object compared)`, `true` for the very same object, `false` for 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 with `equals`, and the number with `==`.\n- A list's `contains`, `indexOf` and `remove(value)` compare with `equals`, so they find a brand-new Seat that's equal to one in the list.\n\n### Seats as hash map keys\n\nA 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`:\n\n```java\nimport java.util.HashMap;\n\npublic class Main {\n    public static void main(String[] args) {\n        HashMap<Seat, String> holders = new HashMap<>();\n        Seat seat = new Seat(\"B\", 7);\n        holders.put(seat, \"Ada\");\n        System.out.println(holders.get(seat));\n        System.out.println(holders.get(new Seat(\"B\", 7)));\n        System.out.println(holders.containsKey(new Seat(\"B\", 7)));\n\n        holders.put(new Seat(\"B\", 7), \"Ben\");\n        System.out.println(holders.size());\n    }\n}\n// ==== Seat.java ====\npublic class Seat {\n    private String row;\n    private int number;\n\n    public Seat(String row, int number) {\n        this.row = row;\n        this.number = number;\n    }\n\n    public boolean equals(Object compared) {\n        if (this == compared) {\n            return true;\n        }\n        if (!(compared instanceof Seat)) {\n            return false;\n        }\n        Seat other = (Seat) compared;\n        return this.row.equals(other.row) && this.number == other.number;\n    }\n\n    public String toString() {\n        return this.row + this.number;\n    }\n}\n```\n\n```output\nAda\nnull\nfalse\n2\n```\n\n- With the very object that was put in, `get` finds Ada.\n- `new Seat(\"B\", 7)` is equal to that key, and a list would have found it. The map doesn't: `get` gives `null`, and `containsKey` gives `false`.\n- It gets worse. A `put` with 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.\n\nA 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.\n","task":"A board game puts colored tokens on the squares of a grid, with names like `A1`: a column letter and a row number. `Square` has an `equals`, but no `hashCode`. What does this program print?\n","kind":"predict","seed":"import java.util.ArrayList;\nimport java.util.HashMap;\n\npublic class Main {\n    public static void main(String[] args) {\n        Square corner = new Square(\"A\", 1);\n        ArrayList<Square> taken = new ArrayList<>();\n        taken.add(corner);\n        HashMap<Square, String> tokens = new HashMap<>();\n        tokens.put(corner, \"red\");\n        tokens.put(new Square(\"A\", 1), \"blue\");\n\n        System.out.println(taken.contains(new Square(\"A\", 1)));\n        System.out.println(tokens.get(corner));\n        System.out.println(tokens.containsKey(new Square(\"A\", 1)));\n        System.out.println(tokens.size());\n    }\n}\n// ==== Square.java ====\npublic class Square {\n    private String column;\n    private int row;\n\n    public Square(String column, int row) {\n        this.column = column;\n        this.row = row;\n    }\n\n    public boolean equals(Object compared) {\n        if (this == compared) {\n            return true;\n        }\n        if (!(compared instanceof Square)) {\n            return false;\n        }\n        Square other = (Square) compared;\n        return this.column.equals(other.column) && this.row == other.row;\n    }\n\n    public String toString() {\n        return this.column + this.row;\n    }\n}\n","solution":"import java.util.ArrayList;\nimport java.util.HashMap;\n\npublic class Main {\n    public static void main(String[] args) {\n        Square corner = new Square(\"A\", 1);\n        ArrayList<Square> taken = new ArrayList<>();\n        taken.add(corner);\n        HashMap<Square, String> tokens = new HashMap<>();\n        tokens.put(corner, \"red\");\n        tokens.put(new Square(\"A\", 1), \"blue\");\n\n        System.out.println(taken.contains(new Square(\"A\", 1)));\n        System.out.println(tokens.get(corner));\n        System.out.println(tokens.containsKey(new Square(\"A\", 1)));\n        System.out.println(tokens.size());\n    }\n}\n// ==== Square.java ====\npublic class Square {\n    private String column;\n    private int row;\n\n    public Square(String column, int row) {\n        this.column = column;\n        this.row = row;\n    }\n\n    public boolean equals(Object compared) {\n        if (this == compared) {\n            return true;\n        }\n        if (!(compared instanceof Square)) {\n            return false;\n        }\n        Square other = (Square) compared;\n        return this.column.equals(other.column) && this.row == other.row;\n    }\n\n    public String toString() {\n        return this.column + this.row;\n    }\n}\n","hints":["The list compares with `equals`, and a new `Square(\"A\", 1)` is equal to `corner`. `tokens.get(corner)` looks up the very object that was put in with `red`.","Without a `hashCode` of its own, every new Square gets Java's default hash code, which in practice is different for every object. So to the map, each new A1 almost certainly looks like a different key: the `put` with a new A1 adds a second key instead of replacing `red`, and `containsKey` with a third new A1 finds nothing."],"tests":[{"name":"Output","stdin":"","expect":"true\nred\nfalse\n2","hidden":false}],"require":[],"forbid":[],"lines":["true","red","false","2"],"more":[{"task":"A coin collector describes each coin by its country, its year and its value in cents. Two coins are equal when all three are the same. In `Coin.java`, write the method `equals` the classic way, as in the recap. The check runs:\n\n```java\nCoin mine = new Coin(\"Italy\", 2002, 50);\nSystem.out.println(mine.equals(new Coin(\"Italy\", 2002, 50)));\nSystem.out.println(mine.equals(new Coin(\"Italy\", 2002, 20)));\nArrayList<Coin> album = new ArrayList<>();\nalbum.add(new Coin(\"Kenya\", 2005, 10));\nalbum.add(mine);\nSystem.out.println(album.indexOf(new Coin(\"Italy\", 2002, 50)));\n```\n\nand expects:\n\n```\ntrue\nfalse\n1\n```\n","kind":"code","seed":"// ==== Coin.java ====\npublic class Coin {\n    private String country;\n    private int year;\n    private int cents;\n\n    public Coin(String country, int year, int cents) {\n        this.country = country;\n        this.year = year;\n        this.cents = cents;\n    }\n\n    // Write the equals method here.\n\n    public String toString() {\n        return this.country + \" \" + this.year + \" \" + this.cents + \"c\";\n    }\n}\n\n// ==== Main.java ====\nimport java.util.ArrayList;\n\npublic class Main {\n    public static void main(String[] args) {\n        Coin mine = new Coin(\"Italy\", 2002, 50);\n        System.out.println(mine.equals(new Coin(\"Italy\", 2002, 50)));\n        System.out.println(mine.equals(new Coin(\"Italy\", 2002, 20)));\n        ArrayList<Coin> album = new ArrayList<>();\n        album.add(new Coin(\"Kenya\", 2005, 10));\n        album.add(mine);\n        System.out.println(album.indexOf(new Coin(\"Italy\", 2002, 50)));\n    }\n}\n","solution":"// ==== Coin.java ====\npublic class Coin {\n    private String country;\n    private int year;\n    private int cents;\n\n    public Coin(String country, int year, int cents) {\n        this.country = country;\n        this.year = year;\n        this.cents = cents;\n    }\n\n    public boolean equals(Object compared) {\n        if (this == compared) {\n            return true;\n        }\n        if (!(compared instanceof Coin)) {\n            return false;\n        }\n        Coin other = (Coin) compared;\n        return this.country.equals(other.country) && this.year == other.year && this.cents == other.cents;\n    }\n\n    public String toString() {\n        return this.country + \" \" + this.year + \" \" + this.cents + \"c\";\n    }\n}\n\n// ==== Main.java ====\nimport java.util.ArrayList;\n\npublic class Main {\n    public static void main(String[] args) {\n        Coin mine = new Coin(\"Italy\", 2002, 50);\n        System.out.println(mine.equals(new Coin(\"Italy\", 2002, 50)));\n        System.out.println(mine.equals(new Coin(\"Italy\", 2002, 20)));\n        ArrayList<Coin> album = new ArrayList<>();\n        album.add(new Coin(\"Kenya\", 2005, 10));\n        album.add(mine);\n        System.out.println(album.indexOf(new Coin(\"Italy\", 2002, 50)));\n    }\n}\n","hints":["Follow Seat's `equals`: the header `public boolean equals(Object compared)`, the `this == compared` check, `instanceof Coin`, and the cast `Coin other = (Coin) compared;`.","The last line compares all three values: `return this.country.equals(other.country) && this.year == other.year && this.cents == other.cents;`. The country is a String, so it's compared with `equals`."],"tests":[{"name":"Test 1","stdin":"","call":"Coin mine = new Coin(\"Italy\", 2002, 50);\nSystem.out.println(mine.equals(new Coin(\"Italy\", 2002, 50)));\nSystem.out.println(mine.equals(new Coin(\"Italy\", 2002, 20)));\nArrayList<Coin> album = new ArrayList<>();\nalbum.add(new Coin(\"Kenya\", 2005, 10));\nalbum.add(mine);\nSystem.out.println(album.indexOf(new Coin(\"Italy\", 2002, 50)));\n","expect":"true\nfalse\n1","hidden":false},{"name":"Test 2","stdin":"","call":"Coin coin = new Coin(\"Kenya\", 2005, 10);\nSystem.out.println(coin.equals(new Coin(\"Kenya\", 2006, 10)));\nSystem.out.println(coin.equals(new Coin(\"Chile\", 2005, 10)));\nSystem.out.println(coin.equals(coin));\nSystem.out.println(coin.equals(\"Kenya 2005 10c\"));\nSystem.out.println(coin.equals(null));\n","expect":"false\nfalse\ntrue\nfalse\nfalse","hidden":true},{"name":"Test 3","stdin":"","call":"String start = \"Ken\";\nCoin built = new Coin(start + \"ya\", 2005, 10);\nCoin written = new Coin(\"Kenya\", 2005, 10);\nSystem.out.println(built.equals(written) + \" \" + written.equals(built));\nArrayList<Coin> album = new ArrayList<>();\nalbum.add(written);\nalbum.remove(built);\nSystem.out.println(album.size());\n","expect":"true true\n0","hidden":true}],"require":[{"pattern":"\\bpublic\\s+boolean\\s+equals\\s*\\(\\s*Object\\s+\\w+\\s*\\)","message":"Write the header exactly as `public boolean equals(Object compared)`, so that lists and maps use your method."},{"pattern":"\\binstanceof\\s+Coin\\b","message":"Check with `instanceof Coin` that the compared object is a coin, before the cast."}],"forbid":[]},{"task":"A drawing program keeps the color of every pixel of a small picture in a list. A color is three numbers from 0 to 255: how much red, green and blue it has. In `Color.java`, write `equals`: two colors are equal when all three numbers are the same. Then, in `Main.java`, write the method `countColors(ArrayList<Color> pixels)`, which returns how many different colors the picture uses. Keep a second list of the colors seen so far, and add a pixel's color to it only when an equal color isn't there yet. The check runs:\n\n```java\nArrayList<Color> pixels = new ArrayList<>();\npixels.add(new Color(255, 0, 0));\npixels.add(new Color(0, 0, 255));\npixels.add(new Color(255, 0, 0));\npixels.add(new Color(255, 255, 255));\npixels.add(new Color(0, 0, 255));\nSystem.out.println(countColors(pixels));\n```\n\nand expects:\n\n```\n3\n```\n","kind":"code","seed":"// ==== Color.java ====\npublic class Color {\n    private int red;\n    private int green;\n    private int blue;\n\n    public Color(int red, int green, int blue) {\n        this.red = red;\n        this.green = green;\n        this.blue = blue;\n    }\n\n    // Write the equals method here.\n\n    public String toString() {\n        return \"(\" + this.red + \", \" + this.green + \", \" + this.blue + \")\";\n    }\n}\n\n// ==== Main.java ====\nimport java.util.ArrayList;\n\npublic class Main {\n    public static void main(String[] args) {\n        ArrayList<Color> pixels = new ArrayList<>();\n        pixels.add(new Color(255, 0, 0));\n        pixels.add(new Color(0, 0, 255));\n        pixels.add(new Color(255, 0, 0));\n        pixels.add(new Color(255, 255, 255));\n        pixels.add(new Color(0, 0, 255));\n        System.out.println(countColors(pixels));\n    }\n\n    // Write the method countColors here.\n}\n","solution":"// ==== Color.java ====\npublic class Color {\n    private int red;\n    private int green;\n    private int blue;\n\n    public Color(int red, int green, int blue) {\n        this.red = red;\n        this.green = green;\n        this.blue = blue;\n    }\n\n    public boolean equals(Object compared) {\n        if (this == compared) {\n            return true;\n        }\n        if (!(compared instanceof Color)) {\n            return false;\n        }\n        Color other = (Color) compared;\n        return this.red == other.red && this.green == other.green && this.blue == other.blue;\n    }\n\n    public String toString() {\n        return \"(\" + this.red + \", \" + this.green + \", \" + this.blue + \")\";\n    }\n}\n\n// ==== Main.java ====\nimport java.util.ArrayList;\n\npublic class Main {\n    public static void main(String[] args) {\n        ArrayList<Color> pixels = new ArrayList<>();\n        pixels.add(new Color(255, 0, 0));\n        pixels.add(new Color(0, 0, 255));\n        pixels.add(new Color(255, 0, 0));\n        pixels.add(new Color(255, 255, 255));\n        pixels.add(new Color(0, 0, 255));\n        System.out.println(countColors(pixels));\n    }\n\n    public static int countColors(ArrayList<Color> pixels) {\n        ArrayList<Color> seen = new ArrayList<>();\n        for (Color pixel : pixels) {\n            if (!seen.contains(pixel)) {\n                seen.add(pixel);\n            }\n        }\n        return seen.size();\n    }\n}\n","hints":["Color's `equals` is the classic one, with Color in place of Seat. All three values are ints, so the last line is `return this.red == other.red && this.green == other.green && this.blue == other.blue;`.","In `countColors`, make an `ArrayList<Color> seen`, go through the pixels with a for-each loop, and add a pixel to `seen` when `!seen.contains(pixel)`. The answer is `seen.size()`. Without `equals`, `contains` finds nothing, and every pixel counts as a new color."],"tests":[{"name":"Test 1","stdin":"","call":"ArrayList<Color> pixels = new ArrayList<>();\npixels.add(new Color(255, 0, 0));\npixels.add(new Color(0, 0, 255));\npixels.add(new Color(255, 0, 0));\npixels.add(new Color(255, 255, 255));\npixels.add(new Color(0, 0, 255));\nSystem.out.println(countColors(pixels));\n","expect":"3","hidden":false},{"name":"Test 2","stdin":"","call":"ArrayList<Color> pixels = new ArrayList<>();\nSystem.out.println(countColors(pixels));\nfor (int i = 0; i < 4; i++) {\n    pixels.add(new Color(10, 20, 30));\n}\nSystem.out.println(countColors(pixels));\npixels.add(new Color(10, 20, 31));\npixels.add(new Color(30, 20, 10));\npixels.add(new Color(10, 21, 30));\nSystem.out.println(countColors(pixels) + \" \" + pixels.size());\n","expect":"0\n1\n4 7","hidden":true},{"name":"Test 3","stdin":"","call":"Color gray = new Color(128, 128, 128);\nSystem.out.println(gray.equals(new Color(128, 128, 128)));\nSystem.out.println(gray.equals(new Color(128, 128, 0)));\nSystem.out.println(gray.equals(\"gray\"));\nSystem.out.println(gray.equals(null));\n","expect":"true\nfalse\nfalse\nfalse","hidden":true}],"require":[{"pattern":"\\bpublic\\s+boolean\\s+equals\\s*\\(\\s*Object\\s+\\w+\\s*\\)","message":"Write the header exactly as `public boolean equals(Object compared)`, so that `contains` uses your method."},{"pattern":"\\bpublic\\s+static\\s+int\\s+countColors\\s*\\(\\s*ArrayList<Color>\\s+\\w+\\s*\\)","message":"Write the method as `public static int countColors(ArrayList<Color> pixels)`."}],"forbid":[]}]}