{"id":"sorting-objects-comparable","slug":"comparable","title":"Comparable: a natural order for your class","text":"Module 28 sorted lists of numbers and Strings with `Collections.sort`, and said that a list of your own objects can't be sorted that way yet. Java knows that 3 comes before 7 and that \"Ada\" comes before \"Ben\". But a conference talk: does it go by its title, its start time or its room? Java can't guess. This module shows how to tell it.\n\n### Java doesn't know the order\n\nA conference app keeps the day's talks. Each talk has a title and the hour it starts. Sorting them doesn't compile:\n\n```java\nimport java.util.ArrayList;\nimport java.util.Collections;\n\npublic class Main {\n    public static void main(String[] args) {\n        ArrayList<Talk> talks = new ArrayList<>();\n        talks.add(new Talk(\"Testing with JUnit\", 14));\n        talks.add(new Talk(\"Opening keynote\", 9));\n        Collections.sort(talks);\n        System.out.println(talks);\n    }\n}\n// ==== Talk.java ====\npublic class Talk {\n    private String title;\n    private int hour;\n\n    public Talk(String title, int hour) {\n        this.title = title;\n        this.hour = hour;\n    }\n\n    @Override\n    public String toString() {\n        return this.hour + \":00 \" + this.title;\n    }\n}\n```\n\n```javac\nMain.java:9: error: no suitable method found for sort(ArrayList<Talk>)\n        Collections.sort(talks);\n                   ^\n    method Collections.<T#1>sort(List<T#1>) is not applicable\n      (inference variable T#1 has incompatible bounds\n        equality constraints: Talk\n        upper bounds: Comparable<? super T#1>)\n    method Collections.<T#2>sort(List<T#2>,Comparator<? super T#2>) is not applicable\n      (cannot infer type-variable(s) T#2\n        (actual and formal argument lists differ in length))\n  where T#1,T#2 are type-variables:\n    T#1 extends Comparable<? super T#1> declared in method <T#1>sort(List<T#1>)\n    T#2 extends Object declared in method <T#2>sort(List<T#2>,Comparator<? super T#2>)\n1 error\n```\n\nThe message is long, but its key word is `Comparable`: `Collections.sort` sorts only elements that are **Comparable**, and a Talk isn't. (The other method it mentions, the one with a `Comparator`, is step 2's.) A stream's `sorted()` from module 37 has the same need. It compiles, but as soon as the stream has two talks to compare, the program stops with a `ClassCastException`.\n\n### Comparable and compareTo\n\n`Comparable` is one of Java's own interfaces (module 35). It lists one method, and in short it looks like this:\n\n```java\npublic interface Comparable<T> {\n    int compareTo(T other);\n}\n```\n\n`T` stands for a type, given in angle brackets as in `ArrayList<Talk>`. A class that implements `Comparable<Talk>` promises the method `compareTo(Talk other)`. It compares the object it's called on, `this`, with `other`, and answers with an int:\n\n- a **negative** number: `this` comes before `other`\n- **zero**: they have the same place in the order\n- a **positive** number: `this` comes after `other`\n\nThe order that `compareTo` gives is called the class's **natural order**. Talks, in the order they start:\n\n```java\nimport java.util.ArrayList;\nimport java.util.Collections;\n\npublic class Main {\n    public static void main(String[] args) {\n        ArrayList<Talk> talks = new ArrayList<>();\n        talks.add(new Talk(\"Clean code in practice\", 11));\n        talks.add(new Talk(\"Opening keynote\", 9));\n        talks.add(new Talk(\"Testing with JUnit\", 14));\n        talks.add(new Talk(\"Accessible web forms\", 11));\n        talks.add(new Talk(\"Streams in depth\", 10));\n        Collections.sort(talks);\n        for (Talk talk : talks) {\n            System.out.println(talk);\n        }\n    }\n}\n// ==== Talk.java ====\npublic class Talk implements Comparable<Talk> {\n    private String title;\n    private int hour;\n\n    public Talk(String title, int hour) {\n        this.title = title;\n        this.hour = hour;\n    }\n\n    public String getTitle() {\n        return this.title;\n    }\n\n    public int getHour() {\n        return this.hour;\n    }\n\n    @Override\n    public int compareTo(Talk other) {\n        if (this.hour < other.hour) {\n            return -1;\n        }\n        if (this.hour > other.hour) {\n            return 1;\n        }\n        return 0;\n    }\n\n    @Override\n    public String toString() {\n        return this.hour + \":00 \" + this.title;\n    }\n}\n```\n\n```output\n9:00 Opening keynote\n10:00 Streams in depth\n11:00 Clean code in practice\n11:00 Accessible web forms\n14:00 Testing with JUnit\n```\n\n- `implements Comparable<Talk>` says that a Talk can be compared with another Talk. `@Override` goes above `compareTo`, as above any method an interface asks for.\n- `compareTo` can read `other.hour`, a private variable of another Talk, because the code is inside the class Talk. Module 32's `equals` did the same.\n- You don't call `compareTo` yourself here. `Collections.sort` calls it on pairs of talks, as many times as it needs, and moves the talks into order.\n- The two talks at 11 are a tie: `compareTo` says 0. Java's sort never swaps elements that tie, so they keep the order they had in the list. Clean code was added first, so it stays first.\n\n### Only the sign matters\n\nA sort only looks at whether the answer is negative, zero or positive, so any negative number means \"before\", not just -1. Java has a method that compares two ints that way, `Integer.compare(a, b)`:\n\n```java\nSystem.out.println(Integer.compare(9, 14));\nSystem.out.println(Integer.compare(14, 14));\nSystem.out.println(Integer.compare(14, 9));\n```\n\n```output\n-1\n0\n1\n```\n\nWith it, the talks' `compareTo` is one line:\n\n```java\n@Override\npublic int compareTo(Talk other) {\n    return Integer.compare(this.hour, other.hour);\n}\n```\n\nSwap the two values, `Integer.compare(other.hour, this.hour)`, and the order turns around: the latest talk comes first.\n\nFor `double` values, `Double.compare(a, b)` works the same way.\n\nYou'll also see `return this.hour - other.hour;` in other people's code. For small numbers such as hours, the difference has the right sign. But a subtraction can overflow (module 4): when the numbers are far apart, the result wraps around, and the sign comes out wrong:\n\n```java\nint big = 2000000000;\nint small = -2000000000;\nSystem.out.println(big - small);\nSystem.out.println(Integer.compare(big, small));\n```\n\n```output\n-294967296\n1\n```\n\n`big` is the larger number, so the answer should be positive, but the subtraction says negative. `Integer.compare` is always right, so this course uses it.\n\n### Strings have a compareTo already\n\nString implements `Comparable<String>`, and its `compareTo` gives alphabetical order. That's what `Collections.sort` used for Strings in module 28:\n\n```java\nSystem.out.println(\"Ada\".compareTo(\"Ben\") < 0);\nSystem.out.println(\"Ben\".compareTo(\"Ada\") < 0);\nSystem.out.println(\"Ada\".compareTo(\"Ada\"));\nSystem.out.println(\"Zoe\".compareTo(\"adam\") < 0);\n```\n\n```output\ntrue\nfalse\n0\ntrue\n```\n\nThe last line is a surprise: \"Zoe\" comes before \"adam\". Strings are compared letter by letter, by the letters' codes, and every capital from A to Z comes before every small letter from a to z. Names made of a capital from A to Z followed by small letters from a to z, like Ada and Ben, sort as you'd expect.\n\nA class ordered by a String, such as talks by their titles, hands the job to the String's `compareTo`:\n\n```java\n@Override\npublic int compareTo(Talk other) {\n    return this.title.compareTo(other.title);\n}\n```\n\n### sorted() in a stream\n\nModule 37 promised this: once a class is Comparable, a stream's `sorted()` works for its objects too. It uses the same `compareTo`, and it leaves the list as it was:\n\n```java\nimport java.util.ArrayList;\n\npublic class Main {\n    public static void main(String[] args) {\n        ArrayList<Talk> talks = new ArrayList<>();\n        talks.add(new Talk(\"Clean code in practice\", 11));\n        talks.add(new Talk(\"Opening keynote\", 9));\n        talks.add(new Talk(\"Streams in depth\", 10));\n        talks.stream()\n                .sorted()\n                .forEach(System.out::println);\n        System.out.println(\"First in the list: \" + talks.get(0));\n    }\n}\n// ==== Talk.java ====\npublic class Talk implements Comparable<Talk> {\n    private String title;\n    private int hour;\n\n    public Talk(String title, int hour) {\n        this.title = title;\n        this.hour = hour;\n    }\n\n    @Override\n    public int compareTo(Talk other) {\n        return Integer.compare(this.hour, other.hour);\n    }\n\n    @Override\n    public String toString() {\n        return this.hour + \":00 \" + this.title;\n    }\n}\n```\n\n```output\n9:00 Opening keynote\n10:00 Streams in depth\n11:00 Clean code in practice\nFirst in the list: 11:00 Clean code in practice\n```\n","task":"A rental site lists apartments, the cheapest first. `Apartment.java` has a street and a monthly rent. Make Apartment implement `Comparable<Apartment>`, with a `compareTo` that orders apartments by rent, the cheapest first. Use `Integer.compare`, and put `@Override` above the method. Compare only the rents: two apartments with the same rent are a tie, so `compareTo` gives 0 and the sort keeps them in the list's order. The check runs:\n\n```java\nArrayList<Apartment> listings = new ArrayList<>();\nlistings.add(new Apartment(\"12 Elm Street\", 1450));\nlistings.add(new Apartment(\"3 Harbor Lane\", 980));\nlistings.add(new Apartment(\"47 Hill Road\", 1200));\nCollections.sort(listings);\nSystem.out.println(listings);\n```\n\nand expects:\n\n```\n[3 Harbor Lane (980), 47 Hill Road (1200), 12 Elm Street (1450)]\n```\n","kind":"code","seed":"// ==== Apartment.java ====\npublic class Apartment {\n    private String street;\n    private int rent;\n\n    public Apartment(String street, int rent) {\n        this.street = street;\n        this.rent = rent;\n    }\n\n    public String getStreet() {\n        return this.street;\n    }\n\n    public int getRent() {\n        return this.rent;\n    }\n\n    @Override\n    public String toString() {\n        return this.street + \" (\" + this.rent + \")\";\n    }\n}\n\n// ==== Main.java ====\nimport java.util.ArrayList;\nimport java.util.Collections;\n\npublic class Main {\n    public static void main(String[] args) {\n        ArrayList<Apartment> listings = new ArrayList<>();\n        listings.add(new Apartment(\"12 Elm Street\", 1450));\n        listings.add(new Apartment(\"3 Harbor Lane\", 980));\n        listings.add(new Apartment(\"47 Hill Road\", 1200));\n        Collections.sort(listings);\n        System.out.println(listings);\n    }\n}\n","solution":"// ==== Apartment.java ====\npublic class Apartment implements Comparable<Apartment> {\n    private String street;\n    private int rent;\n\n    public Apartment(String street, int rent) {\n        this.street = street;\n        this.rent = rent;\n    }\n\n    public String getStreet() {\n        return this.street;\n    }\n\n    public int getRent() {\n        return this.rent;\n    }\n\n    @Override\n    public int compareTo(Apartment other) {\n        return Integer.compare(this.rent, other.rent);\n    }\n\n    @Override\n    public String toString() {\n        return this.street + \" (\" + this.rent + \")\";\n    }\n}\n\n// ==== Main.java ====\nimport java.util.ArrayList;\nimport java.util.Collections;\n\npublic class Main {\n    public static void main(String[] args) {\n        ArrayList<Apartment> listings = new ArrayList<>();\n        listings.add(new Apartment(\"12 Elm Street\", 1450));\n        listings.add(new Apartment(\"3 Harbor Lane\", 980));\n        listings.add(new Apartment(\"47 Hill Road\", 1200));\n        Collections.sort(listings);\n        System.out.println(listings);\n    }\n}\n","hints":["Change the header to `public class Apartment implements Comparable<Apartment> {`. Then the class must have the method `public int compareTo(Apartment other)`.","The cheapest comes first, so `this` goes before `other` when its rent is smaller: `return Integer.compare(this.rent, other.rent);`."],"tests":[{"name":"Test 1","stdin":"","call":"ArrayList<Apartment> listings = new ArrayList<>();\nlistings.add(new Apartment(\"12 Elm Street\", 1450));\nlistings.add(new Apartment(\"3 Harbor Lane\", 980));\nlistings.add(new Apartment(\"47 Hill Road\", 1200));\nCollections.sort(listings);\nSystem.out.println(listings);\n","expect":"[3 Harbor Lane (980), 47 Hill Road (1200), 12 Elm Street (1450)]","hidden":false},{"name":"Test 2","stdin":"","call":"ArrayList<Apartment> listings = new ArrayList<>();\nlistings.add(new Apartment(\"8 Mill Road\", 2100));\nlistings.add(new Apartment(\"5 Oak Court\", 750));\nlistings.add(new Apartment(\"21 Bay View\", 2100));\nlistings.add(new Apartment(\"2 Park Row\", 1999));\nlistings.add(new Apartment(\"6 Pier Street\", 750));\nCollections.sort(listings);\nSystem.out.println(listings);\n","expect":"[5 Oak Court (750), 6 Pier Street (750), 2 Park Row (1999), 8 Mill Road (2100), 21 Bay View (2100)]","hidden":true},{"name":"Test 3","stdin":"","call":"Apartment cheap = new Apartment(\"1 Low Street\", 500);\nApartment dear = new Apartment(\"99 High Street\", 3000);\nSystem.out.println(cheap.compareTo(dear) < 0);\nSystem.out.println(dear.compareTo(cheap) > 0);\nSystem.out.println(cheap.compareTo(new Apartment(\"3 Other Street\", 500)));\n","expect":"true\ntrue\n0","hidden":true}],"require":[{"pattern":"\\bclass\\s+Apartment\\s+implements\\s+Comparable\\s*<\\s*Apartment\\s*>","message":"Write the header as `public class Apartment implements Comparable<Apartment>`."},{"pattern":"\\bInteger\\s*\\.\\s*compare\\s*\\(","message":"Compare the rents with `Integer.compare`."},{"pattern":"@Override\\s+public\\s+int\\s+compareTo\\s*\\(","message":"Put `@Override` above `public int compareTo(Apartment other)`."}],"forbid":[],"more":[{"task":"A hackathon's scoreboard shows the teams with the highest score first. Look closely at the order of the two values in `compareTo`. What does this program print?\n","kind":"predict","seed":"import java.util.ArrayList;\nimport java.util.Collections;\n\npublic class Main {\n    public static void main(String[] args) {\n        ArrayList<Entry> board = new ArrayList<>();\n        board.add(new Entry(\"Byte Club\", 72));\n        board.add(new Entry(\"Null Pointers\", 88));\n        board.add(new Entry(\"Stack Stars\", 72));\n        board.add(new Entry(\"Loop Troop\", 95));\n        Collections.sort(board);\n        for (Entry entry : board) {\n            System.out.println(entry);\n        }\n        System.out.println(board.get(3).compareTo(board.get(0)) > 0);\n    }\n}\n// ==== Entry.java ====\npublic class Entry implements Comparable<Entry> {\n    private String team;\n    private int score;\n\n    public Entry(String team, int score) {\n        this.team = team;\n        this.score = score;\n    }\n\n    @Override\n    public int compareTo(Entry other) {\n        return Integer.compare(other.score, this.score);\n    }\n\n    @Override\n    public String toString() {\n        return this.team + \" \" + this.score;\n    }\n}\n","solution":"import java.util.ArrayList;\nimport java.util.Collections;\n\npublic class Main {\n    public static void main(String[] args) {\n        ArrayList<Entry> board = new ArrayList<>();\n        board.add(new Entry(\"Byte Club\", 72));\n        board.add(new Entry(\"Null Pointers\", 88));\n        board.add(new Entry(\"Stack Stars\", 72));\n        board.add(new Entry(\"Loop Troop\", 95));\n        Collections.sort(board);\n        for (Entry entry : board) {\n            System.out.println(entry);\n        }\n        System.out.println(board.get(3).compareTo(board.get(0)) > 0);\n    }\n}\n// ==== Entry.java ====\npublic class Entry implements Comparable<Entry> {\n    private String team;\n    private int score;\n\n    public Entry(String team, int score) {\n        this.team = team;\n        this.score = score;\n    }\n\n    @Override\n    public int compareTo(Entry other) {\n        return Integer.compare(other.score, this.score);\n    }\n\n    @Override\n    public String toString() {\n        return this.team + \" \" + this.score;\n    }\n}\n","hints":["`Integer.compare(other.score, this.score)` has the two values swapped: it's negative when `this` has the *higher* score. So a higher score comes first.","The order is 95, 88, 72, 72. Byte Club and Stack Stars tie at 72, and a tie keeps the list's order. Last, Stack Stars comes after Loop Troop, so its `compareTo` is positive."],"tests":[{"name":"Output","stdin":"","expect":"Loop Troop 95\nNull Pointers 88\nByte Club 72\nStack Stars 72\ntrue","hidden":false}],"require":[],"forbid":[],"lines":["Loop Troop 95","Null Pointers 88","Byte Club 72","Stack Stars 72","true"]},{"task":"A spice rack is easier to use in alphabetical order. `Spice.java` has a name and the grams left. Write:\n\n- in `Spice.java`: make Spice implement `Comparable<Spice>`, with a `compareTo` that orders spices by name, alphabetically. Compare only the names: two spices with the same name are a tie, so `compareTo` gives 0 and they keep the list's order.\n- in `Main.java`: the method `printRack(List<Spice> rack)`, which prints the spices one per line in alphabetical order, without changing the list. Use a stream with `sorted()`.\n\nThe check runs:\n\n```java\nList<Spice> rack = new ArrayList<>();\nrack.add(new Spice(\"Paprika\", 50));\nrack.add(new Spice(\"Cumin\", 40));\nrack.add(new Spice(\"Saffron\", 2));\nrack.add(new Spice(\"Cardamom\", 25));\nprintRack(rack);\nSystem.out.println(rack.get(0));\n```\n\nand expects:\n\n```\nCardamom (25 g)\nCumin (40 g)\nPaprika (50 g)\nSaffron (2 g)\nPaprika (50 g)\n```\n","kind":"code","seed":"// ==== Spice.java ====\npublic class Spice {\n    private String name;\n    private int grams;\n\n    public Spice(String name, int grams) {\n        this.name = name;\n        this.grams = grams;\n    }\n\n    public String getName() {\n        return this.name;\n    }\n\n    public int getGrams() {\n        return this.grams;\n    }\n\n    @Override\n    public String toString() {\n        return this.name + \" (\" + this.grams + \" g)\";\n    }\n}\n\n// ==== Main.java ====\nimport java.util.ArrayList;\nimport java.util.List;\n\npublic class Main {\n    public static void main(String[] args) {\n        List<Spice> rack = new ArrayList<>();\n        rack.add(new Spice(\"Paprika\", 50));\n        rack.add(new Spice(\"Cumin\", 40));\n        rack.add(new Spice(\"Saffron\", 2));\n        rack.add(new Spice(\"Cardamom\", 25));\n        printRack(rack);\n        System.out.println(rack.get(0));\n    }\n\n    // Write the method printRack here.\n}\n","solution":"// ==== Spice.java ====\npublic class Spice implements Comparable<Spice> {\n    private String name;\n    private int grams;\n\n    public Spice(String name, int grams) {\n        this.name = name;\n        this.grams = grams;\n    }\n\n    public String getName() {\n        return this.name;\n    }\n\n    public int getGrams() {\n        return this.grams;\n    }\n\n    @Override\n    public int compareTo(Spice other) {\n        return this.name.compareTo(other.name);\n    }\n\n    @Override\n    public String toString() {\n        return this.name + \" (\" + this.grams + \" g)\";\n    }\n}\n\n// ==== Main.java ====\nimport java.util.ArrayList;\nimport java.util.List;\n\npublic class Main {\n    public static void main(String[] args) {\n        List<Spice> rack = new ArrayList<>();\n        rack.add(new Spice(\"Paprika\", 50));\n        rack.add(new Spice(\"Cumin\", 40));\n        rack.add(new Spice(\"Saffron\", 2));\n        rack.add(new Spice(\"Cardamom\", 25));\n        printRack(rack);\n        System.out.println(rack.get(0));\n    }\n\n    public static void printRack(List<Spice> rack) {\n        rack.stream()\n                .sorted()\n                .forEach(System.out::println);\n    }\n}\n","hints":["The header becomes `public class Spice implements Comparable<Spice> {`. A String already knows alphabetical order, so Spice's `compareTo` can hand the job to the names' own `compareTo`, as the talks did with their titles.","`compareTo` is one line, `return this.name.compareTo(other.name);`. `printRack` is `public static void printRack(List<Spice> rack)`, and its body is one stream: `rack.stream().sorted().forEach(System.out::println);`. `sorted()` uses Spice's `compareTo`."],"tests":[{"name":"Test 1","stdin":"","call":"List<Spice> rack = new ArrayList<>();\nrack.add(new Spice(\"Paprika\", 50));\nrack.add(new Spice(\"Cumin\", 40));\nrack.add(new Spice(\"Saffron\", 2));\nrack.add(new Spice(\"Cardamom\", 25));\nprintRack(rack);\nSystem.out.println(rack.get(0));\n","expect":"Cardamom (25 g)\nCumin (40 g)\nPaprika (50 g)\nSaffron (2 g)\nPaprika (50 g)","hidden":false},{"name":"Test 2","stdin":"","call":"List<Spice> rack = new ArrayList<>();\nprintRack(rack);\nrack.add(new Spice(\"Turmeric\", 70));\nrack.add(new Spice(\"Nutmeg\", 15));\nrack.add(new Spice(\"Turmeric\", 5));\nrack.add(new Spice(\"Anise\", 30));\nrack.add(new Spice(\"Nigella\", 12));\nprintRack(rack);\nSystem.out.println(rack);\n","expect":"Anise (30 g)\nNigella (12 g)\nNutmeg (15 g)\nTurmeric (70 g)\nTurmeric (5 g)\n[Turmeric (70 g), Nutmeg (15 g), Turmeric (5 g), Anise (30 g), Nigella (12 g)]","hidden":true},{"name":"Test 3","stdin":"","call":"Spice cumin = new Spice(\"Cumin\", 40);\nSpice clove = new Spice(\"Clove\", 9);\nSystem.out.println(cumin.compareTo(clove) > 0);\nSystem.out.println(clove.compareTo(cumin) < 0);\nSystem.out.println(cumin.compareTo(new Spice(\"Cumin\", 1)));\n","expect":"true\ntrue\n0","hidden":true}],"require":[{"pattern":"\\bclass\\s+Spice\\s+implements\\s+Comparable\\s*<\\s*Spice\\s*>","message":"Write the header as `public class Spice implements Comparable<Spice>`."},{"pattern":"\\.compareTo\\s*\\(","message":"Compare the names with the Strings' own `compareTo`."},{"pattern":"\\.sorted\\s*\\(\\s*\\)","message":"Sort in the stream with `sorted()`."}],"forbid":[{"pattern":"\\bCollections\\s*\\.\\s*sort\\b|\\.sort\\s*\\(","message":"`printRack` shouldn't change the list, so don't sort the list itself: let the stream sort what passes through it, with `sorted()`."}]}]}