{"id":"sorting-objects-choosing","slug":"choosing","title":"Natural order or comparator?","text":"An order can live in two places: inside the class, as its `compareTo`, or outside it, as a comparator handed to a sort. This step is about choosing between them, and it ends the module with a challenge that uses both.\n\n### An order that belongs to the class\n\nAn app store keeps the releases of an app. Each release has a version number of two parts, such as 2.9 or 2.10, and a download count. Kept as text, the versions sort wrongly:\n\n```java\nArrayList<String> versions = new ArrayList<>();\nversions.add(\"2.9\");\nversions.add(\"10.0\");\nversions.add(\"2.10\");\nCollections.sort(versions);\nSystem.out.println(versions);\n```\n\n```output\n[10.0, 2.10, 2.9]\n```\n\nText is compared character by character. The character 1 comes before 2, so \"10.0\" goes first, and \"2.10\" goes before \"2.9\" because 1 comes before 9. But 2.10 is the release after 2.9, and 10.0 came after both. Versions have one right order, the same everywhere and for everyone. That's what a natural order is for:\n\n```java\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.Comparator;\nimport java.util.List;\n\npublic class Main {\n    public static void main(String[] args) {\n        List<Release> releases = new ArrayList<>();\n        releases.add(new Release(2, 9, 5400));\n        releases.add(new Release(10, 0, 820));\n        releases.add(new Release(2, 10, 7300));\n        releases.add(new Release(1, 4, 12000));\n\n        Collections.sort(releases);\n        System.out.println(\"History: \" + releases);\n\n        releases.sort(Comparator.comparing(Release::getDownloads).reversed());\n        System.out.println(\"Most downloaded: \" + releases);\n    }\n}\n// ==== Release.java ====\npublic class Release implements Comparable<Release> {\n    private int major;\n    private int minor;\n    private int downloads;\n\n    public Release(int major, int minor, int downloads) {\n        this.major = major;\n        this.minor = minor;\n        this.downloads = downloads;\n    }\n\n    public int getDownloads() {\n        return this.downloads;\n    }\n\n    @Override\n    public int compareTo(Release other) {\n        int byMajor = Integer.compare(this.major, other.major);\n        if (byMajor != 0) {\n            return byMajor;\n        }\n        return Integer.compare(this.minor, other.minor);\n    }\n\n    @Override\n    public String toString() {\n        return this.major + \".\" + this.minor;\n    }\n}\n```\n\n```output\nHistory: [1.4, 2.9, 2.10, 10.0]\nMost downloaded: [1.4, 2.10, 2.9, 10.0]\n```\n\n- The version numbers are ints, and `compareTo` compares them as step 3 did: the first part, and for a tie, the second.\n- The download ranking is the order of one page of the store. It changes every day, and it isn't what anyone means by \"the order of the versions\". So it's a comparator, written where that page sorts its list.\n\n### How to choose\n\n- **In the class, with Comparable**, when the objects have one order that everyone expects, the same all over the program: versions, times of day, dates, sizes. Then `Collections.sort(list)` and `sorted()` work with nothing more, and so does anything else in Java that needs an order. A class has only one `compareTo`, so give it the obvious order, or none.\n- **Where you sort, with a Comparator**, for every other order. There can be any number of them, and the class doesn't change: the flights by gate, the releases by downloads, the teas by price and then name. A rule that belongs to one page or one report goes next to the code of that page.\n- **No natural order at all** is fine. Is a hackathon entry \"smaller\" than another when its score is lower, or when its team's name comes first? Neither is obvious, and the contest's ranking rules might change. Such a class is better off without `compareTo`, and every sort says which order it wants. Step 1's hackathon challenge gave Entry a `compareTo` only to show the two values swapped. Step 3's Entry has none, and that's the better design.\n- **A class that isn't yours** keeps its natural order. String's `compareTo` is alphabetical, and you can't change the class String. A comparator can sort Strings in any other order, such as by length.\n- **The natural order backwards** is `Comparator.reverseOrder()`. `releases.sort(Comparator.reverseOrder())` would put the newest version first.\n\nThe last two, on names:\n\n```java\nimport java.util.ArrayList;\nimport java.util.Comparator;\nimport java.util.List;\n\npublic class Main {\n    public static void main(String[] args) {\n        List<String> names = new ArrayList<>();\n        names.add(\"Priya\");\n        names.add(\"Bo\");\n        names.add(\"Alexander\");\n        names.add(\"Ines\");\n        names.sort(Comparator.comparing(String::length));\n        System.out.println(names);\n        names.sort(Comparator.reverseOrder());\n        System.out.println(names);\n    }\n}\n```\n\n```output\n[Bo, Ines, Priya, Alexander]\n[Priya, Ines, Bo, Alexander]\n```\n\n`String::length` is a method of the value itself, like module 37's `String::toUpperCase`, so `Comparator.comparing(String::length)` compares two names by their lengths.\n\n| You want | Use | Examples from this module |\n| --- | --- | --- |\n| The one order everybody expects of the class | `implements Comparable<...>` and `compareTo` | talks by start time, versions by number |\n| Another order, for one list or one page | a lambda, or `Comparator.comparing(...)` | flights by gate, releases by downloads |\n| A second criterion for ties | `thenComparing`, or a second comparison in `compareTo` | talks by hour, then room |\n| The largest first | `reversed()`, or the two values swapped in `Integer.compare` | the hackathon's scoreboard |\n| A sorted view that leaves the list as it is | `sorted()` or `sorted(comparator)` in a stream, or a sorted copy | the podium, the largest videos |\n\n### What this module covered\n\nA class gets a natural order by implementing `Comparable` and its `compareTo`, which answers negative, zero or positive, and `Integer.compare` and String's own `compareTo` do most of the work. `Collections.sort(list)` and a stream's `sorted()` use that order. Any other order is a `Comparator`: a lambda of two objects, or one built with `Comparator.comparing` from a getter, turned around with `reversed()` and given more criteria with `thenComparing`, for `list.sort`, `Collections.sort` and `sorted`. These are the topics of the MOOC's section 10.2. Module 39 finishes part 10 with StringBuilder, regular expressions, enums and iterators.\n","task":"A student's planner lists exams by date. `Exam.java` has a course, a month and a day. Make Exam implement `Comparable<Exam>`, with a `compareTo` that puts exams in date order: by month, and exams in the same month by day. Exams on the same day are a tie, so `compareTo` gives 0 and the sort keeps them in the list's order. Put `@Override` above `compareTo`. The check runs:\n\n```java\nArrayList<Exam> exams = new ArrayList<>();\nexams.add(new Exam(\"Statistics\", 6, 12));\nexams.add(new Exam(\"Algebra\", 5, 28));\nexams.add(new Exam(\"Biology\", 6, 3));\nexams.add(new Exam(\"History\", 5, 30));\nCollections.sort(exams);\nSystem.out.println(exams);\n```\n\nand expects:\n\n```\n[Algebra 5/28, History 5/30, Biology 6/3, Statistics 6/12]\n```\n","kind":"code","seed":"// ==== Exam.java ====\npublic class Exam {\n    private String course;\n    private int month;\n    private int day;\n\n    public Exam(String course, int month, int day) {\n        this.course = course;\n        this.month = month;\n        this.day = day;\n    }\n\n    public String getCourse() {\n        return this.course;\n    }\n\n    @Override\n    public String toString() {\n        return this.course + \" \" + this.month + \"/\" + this.day;\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<Exam> exams = new ArrayList<>();\n        exams.add(new Exam(\"Statistics\", 6, 12));\n        exams.add(new Exam(\"Algebra\", 5, 28));\n        exams.add(new Exam(\"Biology\", 6, 3));\n        exams.add(new Exam(\"History\", 5, 30));\n        Collections.sort(exams);\n        System.out.println(exams);\n    }\n}\n","solution":"// ==== Exam.java ====\npublic class Exam implements Comparable<Exam> {\n    private String course;\n    private int month;\n    private int day;\n\n    public Exam(String course, int month, int day) {\n        this.course = course;\n        this.month = month;\n        this.day = day;\n    }\n\n    public String getCourse() {\n        return this.course;\n    }\n\n    @Override\n    public int compareTo(Exam other) {\n        int byMonth = Integer.compare(this.month, other.month);\n        if (byMonth != 0) {\n            return byMonth;\n        }\n        return Integer.compare(this.day, other.day);\n    }\n\n    @Override\n    public String toString() {\n        return this.course + \" \" + this.month + \"/\" + this.day;\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<Exam> exams = new ArrayList<>();\n        exams.add(new Exam(\"Statistics\", 6, 12));\n        exams.add(new Exam(\"Algebra\", 5, 28));\n        exams.add(new Exam(\"Biology\", 6, 3));\n        exams.add(new Exam(\"History\", 5, 30));\n        Collections.sort(exams);\n        System.out.println(exams);\n    }\n}\n","hints":["The header becomes `public class Exam implements Comparable<Exam> {`, and the class gets `public int compareTo(Exam other)` with `@Override` above it.","Compare the months first: `int byMonth = Integer.compare(this.month, other.month);`. If `byMonth` isn't 0, return it. Otherwise the months are the same, so return `Integer.compare(this.day, other.day)`."],"tests":[{"name":"Test 1","stdin":"","call":"ArrayList<Exam> exams = new ArrayList<>();\nexams.add(new Exam(\"Statistics\", 6, 12));\nexams.add(new Exam(\"Algebra\", 5, 28));\nexams.add(new Exam(\"Biology\", 6, 3));\nexams.add(new Exam(\"History\", 5, 30));\nCollections.sort(exams);\nSystem.out.println(exams);\n","expect":"[Algebra 5/28, History 5/30, Biology 6/3, Statistics 6/12]","hidden":false},{"name":"Test 2","stdin":"","call":"ArrayList<Exam> exams = new ArrayList<>();\nexams.add(new Exam(\"Physics\", 2, 1));\nexams.add(new Exam(\"Chemistry\", 1, 31));\nexams.add(new Exam(\"Latin\", 12, 9));\nexams.add(new Exam(\"Art\", 2, 1));\nexams.add(new Exam(\"Music\", 1, 2));\nexams.add(new Exam(\"Drama\", 11, 30));\nCollections.sort(exams);\nSystem.out.println(exams);\n","expect":"[Music 1/2, Chemistry 1/31, Physics 2/1, Art 2/1, Drama 11/30, Latin 12/9]","hidden":true},{"name":"Test 3","stdin":"","call":"Exam early = new Exam(\"Geology\", 3, 25);\nExam late = new Exam(\"Economics\", 4, 2);\nSystem.out.println(early.compareTo(late) < 0);\nSystem.out.println(late.compareTo(early) > 0);\nSystem.out.println(early.compareTo(new Exam(\"Ethics\", 3, 25)));\nSystem.out.println(new Exam(\"Logic\", 3, 7).compareTo(early) < 0);\n","expect":"true\ntrue\n0\ntrue","hidden":true}],"require":[{"pattern":"\\bclass\\s+Exam\\s+implements\\s+Comparable\\s*<\\s*Exam\\s*>","message":"Write the header as `public class Exam implements Comparable<Exam>`."},{"pattern":"@Override\\s+public\\s+int\\s+compareTo\\s*\\(","message":"Put `@Override` above `public int compareTo(Exam other)`."}],"forbid":[],"more":[{"task":"A tree nursery sorts the names on its labels in several orders. A String's natural order is alphabetical. What does this program print?\n","kind":"predict","seed":"import java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.Comparator;\nimport java.util.List;\n\npublic class Main {\n    public static void main(String[] args) {\n        List<String> trees = new ArrayList<>();\n        trees.add(\"maple\");\n        trees.add(\"oak\");\n        trees.add(\"willow\");\n        trees.add(\"birch\");\n        trees.add(\"elm\");\n        Collections.sort(trees);\n        System.out.println(trees);\n        trees.sort(Comparator.comparing(String::length));\n        System.out.println(trees);\n        trees.sort(Comparator.reverseOrder());\n        System.out.println(trees);\n    }\n}\n","solution":"import java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.Comparator;\nimport java.util.List;\n\npublic class Main {\n    public static void main(String[] args) {\n        List<String> trees = new ArrayList<>();\n        trees.add(\"maple\");\n        trees.add(\"oak\");\n        trees.add(\"willow\");\n        trees.add(\"birch\");\n        trees.add(\"elm\");\n        Collections.sort(trees);\n        System.out.println(trees);\n        trees.sort(Comparator.comparing(String::length));\n        System.out.println(trees);\n        trees.sort(Comparator.reverseOrder());\n        System.out.println(trees);\n    }\n}\n","hints":["The second sort compares only the lengths, so elm and oak tie, and so do birch and maple. A tie keeps the order the list had, and after the first sort that was alphabetical.","`Comparator.reverseOrder()` is the natural order backwards: Z to A, whatever order the list was in before."],"tests":[{"name":"Output","stdin":"","expect":"[birch, elm, maple, oak, willow]\n[elm, oak, birch, maple, willow]\n[willow, oak, maple, elm, birch]","hidden":false}],"require":[],"forbid":[],"lines":["[birch, elm, maple, oak, willow]","[elm, oak, birch, maple, willow]","[willow, oak, maple, elm, birch]"]},{"task":"To end the module, the departures board again, now with a natural order. A board lists flights by time: that's the order anyone expects of it, so it becomes Flight's natural order. The gate staff want another order, so that one is a comparator. Write:\n\n- In `Flight.java`: make Flight implement `Comparable<Flight>`. Flights go by departure time, and flights that leave at the same time by destination, alphabetically.\n- In `Board.java`, the class `Board`, which keeps its flights in a list:\n  - the constructor, and `add(Flight flight)`, which adds a flight to the board\n  - `departures()`, which returns a new list of all the flights in their natural order\n  - `atGate(int gate)`, which returns a new list of the flights from that gate, in their natural order\n  - `byGate()`, which returns a new list of all the flights sorted by gate number, and the flights of the same gate by departure time\n\nThe lists that the methods return are new lists, not the board's own: flights added to the board later don't show up in a list it returned before. `Main.java` is a test drive, and it prints:\n\n```\n[07:45 Oslo (gate 12), 09:05 Riga (gate 12), 09:30 Dublin (gate 4), 13:10 Athens (gate 2), 13:10 Lisbon (gate 4)]\n[07:45 Oslo (gate 12), 09:05 Riga (gate 12)]\n[13:10 Athens (gate 2), 09:30 Dublin (gate 4), 13:10 Lisbon (gate 4), 07:45 Oslo (gate 12), 09:05 Riga (gate 12)]\n```\n","kind":"code","seed":"// ==== Flight.java ====\npublic class Flight {\n    private String destination;\n    private String departs;\n    private int gate;\n\n    public Flight(String destination, String departs, int gate) {\n        this.destination = destination;\n        this.departs = departs;\n        this.gate = gate;\n    }\n\n    public String getDestination() {\n        return this.destination;\n    }\n\n    public String getDeparts() {\n        return this.departs;\n    }\n\n    public int getGate() {\n        return this.gate;\n    }\n\n    @Override\n    public String toString() {\n        return this.departs + \" \" + this.destination + \" (gate \" + this.gate + \")\";\n    }\n}\n\n// ==== Board.java ====\nimport java.util.ArrayList;\nimport java.util.Comparator;\nimport java.util.List;\nimport java.util.stream.Collectors;\n\npublic class Board {\n\n    public Board() {\n\n    }\n\n    public void add(Flight flight) {\n\n    }\n\n    public List<Flight> departures() {\n        return new ArrayList<>();\n    }\n\n    public List<Flight> atGate(int gate) {\n        return new ArrayList<>();\n    }\n\n    public List<Flight> byGate() {\n        return new ArrayList<>();\n    }\n}\n\n// ==== Main.java ====\npublic class Main {\n    public static void main(String[] args) {\n        Board board = new Board();\n        board.add(new Flight(\"Lisbon\", \"13:10\", 4));\n        board.add(new Flight(\"Oslo\", \"07:45\", 12));\n        board.add(new Flight(\"Dublin\", \"09:30\", 4));\n        board.add(new Flight(\"Athens\", \"13:10\", 2));\n        board.add(new Flight(\"Riga\", \"09:05\", 12));\n        System.out.println(board.departures());\n        System.out.println(board.atGate(12));\n        System.out.println(board.byGate());\n    }\n}\n","solution":"// ==== Flight.java ====\npublic class Flight implements Comparable<Flight> {\n    private String destination;\n    private String departs;\n    private int gate;\n\n    public Flight(String destination, String departs, int gate) {\n        this.destination = destination;\n        this.departs = departs;\n        this.gate = gate;\n    }\n\n    public String getDestination() {\n        return this.destination;\n    }\n\n    public String getDeparts() {\n        return this.departs;\n    }\n\n    public int getGate() {\n        return this.gate;\n    }\n\n    @Override\n    public int compareTo(Flight other) {\n        int byTime = this.departs.compareTo(other.departs);\n        if (byTime != 0) {\n            return byTime;\n        }\n        return this.destination.compareTo(other.destination);\n    }\n\n    @Override\n    public String toString() {\n        return this.departs + \" \" + this.destination + \" (gate \" + this.gate + \")\";\n    }\n}\n\n// ==== Board.java ====\nimport java.util.ArrayList;\nimport java.util.Comparator;\nimport java.util.List;\nimport java.util.stream.Collectors;\n\npublic class Board {\n    private List<Flight> flights;\n\n    public Board() {\n        this.flights = new ArrayList<>();\n    }\n\n    public void add(Flight flight) {\n        this.flights.add(flight);\n    }\n\n    public List<Flight> departures() {\n        return this.flights.stream()\n                .sorted()\n                .collect(Collectors.toList());\n    }\n\n    public List<Flight> atGate(int gate) {\n        return this.flights.stream()\n                .filter(flight -> flight.getGate() == gate)\n                .sorted()\n                .collect(Collectors.toList());\n    }\n\n    public List<Flight> byGate() {\n        return this.flights.stream()\n                .sorted(Comparator.comparing(Flight::getGate).thenComparing(Flight::getDeparts))\n                .collect(Collectors.toList());\n    }\n}\n\n// ==== Main.java ====\npublic class Main {\n    public static void main(String[] args) {\n        Board board = new Board();\n        board.add(new Flight(\"Lisbon\", \"13:10\", 4));\n        board.add(new Flight(\"Oslo\", \"07:45\", 12));\n        board.add(new Flight(\"Dublin\", \"09:30\", 4));\n        board.add(new Flight(\"Athens\", \"13:10\", 2));\n        board.add(new Flight(\"Riga\", \"09:05\", 12));\n        System.out.println(board.departures());\n        System.out.println(board.atGate(12));\n        System.out.println(board.byGate());\n    }\n}\n","hints":["Flight's `compareTo` follows step 3's pattern for two criteria. The times are Strings, so their own `compareTo` compares them. In Board, keep a `List<Flight>` that the constructor creates, and let `add` add to it.","Flight's `compareTo` starts with `int byTime = this.departs.compareTo(other.departs);`, returns `byTime` when it isn't 0, and otherwise compares the destinations. In Board, each method can be one stream that ends with `collect(Collectors.toList())`, which makes a new list. `departures` uses `sorted()`, `atGate` puts a `filter` on the gate before `sorted()`, and `byGate` uses `sorted(Comparator.comparing(Flight::getGate).thenComparing(Flight::getDeparts))`."],"tests":[{"name":"The test drive","stdin":"","expect":"[07:45 Oslo (gate 12), 09:05 Riga (gate 12), 09:30 Dublin (gate 4), 13:10 Athens (gate 2), 13:10 Lisbon (gate 4)]\n[07:45 Oslo (gate 12), 09:05 Riga (gate 12)]\n[13:10 Athens (gate 2), 09:30 Dublin (gate 4), 13:10 Lisbon (gate 4), 07:45 Oslo (gate 12), 09:05 Riga (gate 12)]","hidden":false},{"name":"Test 2","stdin":"","call":"Board board = new Board();\nSystem.out.println(board.departures() + \" \" + board.atGate(1) + \" \" + board.byGate());\nboard.add(new Flight(\"Vienna\", \"21:40\", 3));\nboard.add(new Flight(\"Porto\", \"06:15\", 1));\nboard.add(new Flight(\"Tallinn\", \"16:05\", 3));\nboard.add(new Flight(\"Bergen\", \"16:05\", 5));\nboard.add(new Flight(\"Split\", \"11:50\", 3));\nboard.add(new Flight(\"Nice\", \"06:15\", 5));\nSystem.out.println(board.departures());\nSystem.out.println(board.atGate(3));\nSystem.out.println(board.atGate(5));\nSystem.out.println(board.atGate(9));\nSystem.out.println(board.byGate());\n","expect":"[] [] []\n[06:15 Nice (gate 5), 06:15 Porto (gate 1), 11:50 Split (gate 3), 16:05 Bergen (gate 5), 16:05 Tallinn (gate 3), 21:40 Vienna (gate 3)]\n[11:50 Split (gate 3), 16:05 Tallinn (gate 3), 21:40 Vienna (gate 3)]\n[06:15 Nice (gate 5), 16:05 Bergen (gate 5)]\n[]\n[06:15 Porto (gate 1), 11:50 Split (gate 3), 16:05 Tallinn (gate 3), 21:40 Vienna (gate 3), 06:15 Nice (gate 5), 16:05 Bergen (gate 5)]","hidden":true},{"name":"Test 3","stdin":"","call":"Board board = new Board();\nboard.add(new Flight(\"Riga\", \"09:05\", 12));\nList<Flight> before = board.departures();\nList<Flight> gate = board.atGate(12);\nList<Flight> gates = board.byGate();\nboard.add(new Flight(\"Oslo\", \"07:45\", 12));\nSystem.out.println(before + \" \" + gate + \" \" + gates);\nSystem.out.println(board.departures().size() + \" \" + board.atGate(12).size() + \" \" + board.byGate().size());\nList<Flight> flights = new ArrayList<>();\nflights.add(new Flight(\"Zagreb\", \"10:00\", 1));\nflights.add(new Flight(\"Ankara\", \"10:00\", 2));\nflights.add(new Flight(\"Malmo\", \"09:59\", 3));\nCollections.sort(flights);\nSystem.out.println(flights);\nSystem.out.println(flights.get(0).compareTo(flights.get(1)) < 0);\n","expect":"[09:05 Riga (gate 12)] [09:05 Riga (gate 12)] [09:05 Riga (gate 12)]\n2 2 2\n[09:59 Malmo (gate 3), 10:00 Ankara (gate 2), 10:00 Zagreb (gate 1)]\ntrue","hidden":true}],"require":[{"pattern":"\\bclass\\s+Flight\\s+implements\\s+Comparable\\s*<\\s*Flight\\s*>","message":"Write Flight's header as `public class Flight implements Comparable<Flight>`."}],"forbid":[{"pattern":"\\bSystem\\s*\\.\\s*out\\b(?=[\\s\\S]*\\bclass\\s+Main\\b)","message":"Flight and Board don't print. Board's methods return their lists, and `main` prints them."}]}]}