{"id":"sorting-objects-several-criteria","slug":"several-criteria","title":"Several criteria, and sorted streams","text":"The conference has grown: it now runs talks in three rooms at once, so several talks start at the same hour. The printed program lists them by hour, and the talks of the same hour by room, in alphabetical order. The room is a **second criterion**: it's only asked when the first one, the hour, is a tie.\n\n### Two criteria in compareTo\n\n`compareTo` compares the hours first. When they differ, that settles it. Only when they're the same does it go on to the rooms:\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, \"Room B\"));\n        talks.add(new Talk(\"Opening keynote\", 9, \"Main hall\"));\n        talks.add(new Talk(\"Testing with JUnit\", 14, \"Room A\"));\n        talks.add(new Talk(\"Accessible web forms\", 11, \"Room A\"));\n        talks.add(new Talk(\"Streams in depth\", 14, \"Main hall\"));\n        talks.add(new Talk(\"Game jams for beginners\", 11, \"Main hall\"));\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    private String room;\n\n    public Talk(String title, int hour, String room) {\n        this.title = title;\n        this.hour = hour;\n        this.room = room;\n    }\n\n    public String getTitle() {\n        return this.title;\n    }\n\n    public int getHour() {\n        return this.hour;\n    }\n\n    public String getRoom() {\n        return this.room;\n    }\n\n    @Override\n    public int compareTo(Talk other) {\n        int byHour = Integer.compare(this.hour, other.hour);\n        if (byHour != 0) {\n            return byHour;\n        }\n        return this.room.compareTo(other.room);\n    }\n\n    @Override\n    public String toString() {\n        return this.hour + \":00 \" + this.room + \": \" + this.title;\n    }\n}\n```\n\n```output\n9:00 Main hall: Opening keynote\n11:00 Main hall: Game jams for beginners\n11:00 Room A: Accessible web forms\n11:00 Room B: Clean code in practice\n14:00 Main hall: Streams in depth\n14:00 Room A: Testing with JUnit\n```\n\n`byHour` is not 0 when the hours differ, and then it's the answer. The rooms are compared only for talks at the same hour. A third criterion would follow the same pattern: compare the rooms into a variable, return it if it's not 0, and compare the third value last.\n\n### thenComparing\n\nA comparator does the same with `thenComparing`. `Comparator.comparing(Talk::getHour).thenComparing(Talk::getRoom)` compares the hours, and for a tie, the rooms: the order above. Each room's door gets a sign with that room's talks, which is another order, by room first and then by hour:\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<Talk> talks = new ArrayList<>();\n        talks.add(new Talk(\"Clean code in practice\", 11, \"Room B\"));\n        talks.add(new Talk(\"Opening keynote\", 9, \"Main hall\"));\n        talks.add(new Talk(\"Testing with JUnit\", 14, \"Room A\"));\n        talks.add(new Talk(\"Accessible web forms\", 11, \"Room A\"));\n        talks.add(new Talk(\"Streams in depth\", 14, \"Main hall\"));\n        talks.add(new Talk(\"Game jams for beginners\", 11, \"Main hall\"));\n        talks.sort(Comparator.comparing(Talk::getRoom).thenComparing(Talk::getHour));\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    private String room;\n\n    public Talk(String title, int hour, String room) {\n        this.title = title;\n        this.hour = hour;\n        this.room = room;\n    }\n\n    public String getTitle() {\n        return this.title;\n    }\n\n    public int getHour() {\n        return this.hour;\n    }\n\n    public String getRoom() {\n        return this.room;\n    }\n\n    @Override\n    public int compareTo(Talk other) {\n        int byHour = Integer.compare(this.hour, other.hour);\n        if (byHour != 0) {\n            return byHour;\n        }\n        return this.room.compareTo(other.room);\n    }\n\n    @Override\n    public String toString() {\n        return this.hour + \":00 \" + this.room + \": \" + this.title;\n    }\n}\n```\n\n```output\n9:00 Main hall: Opening keynote\n11:00 Main hall: Game jams for beginners\n14:00 Main hall: Streams in depth\n11:00 Room A: Accessible web forms\n14:00 Room A: Testing with JUnit\n11:00 Room B: Clean code in practice\n```\n\n- The criteria are read from left to right. `thenComparing` is asked only when everything before it says \"tie\".\n- More criteria mean more `thenComparing`s: `.thenComparing(Talk::getTitle)` after the hour would sort talks in the same room at the same hour by title.\n- Talk still has its `compareTo`, but a sort that's given a comparator uses the comparator. The natural order is for the sorts that aren't given one.\n\n### reversed in a chain\n\nThe hackathon's scoreboard from step 1's second challenge gets a rule for ties: the highest score first, and teams with the same score in alphabetical order. The score goes from high to low, but the names go from A to Z, so only the first criterion is reversed. Where `reversed()` stands makes the difference:\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<Entry> entries = new ArrayList<>();\n        entries.add(new Entry(\"Byte Club\", 72));\n        entries.add(new Entry(\"Null Pointers\", 88));\n        entries.add(new Entry(\"Stack Stars\", 72));\n        entries.add(new Entry(\"Loop Troop\", 95));\n        entries.add(new Entry(\"Array Aces\", 88));\n\n        entries.sort(Comparator.comparing(Entry::getScore).reversed().thenComparing(Entry::getTeam));\n        System.out.println(entries);\n\n        entries.sort(Comparator.comparing(Entry::getScore).thenComparing(Entry::getTeam).reversed());\n        System.out.println(entries);\n    }\n}\n// ==== Entry.java ====\npublic class 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    public String getTeam() {\n        return this.team;\n    }\n\n    public int getScore() {\n        return this.score;\n    }\n\n    @Override\n    public String toString() {\n        return this.team + \" \" + this.score;\n    }\n}\n```\n\n```output\n[Loop Troop 95, Array Aces 88, Null Pointers 88, Byte Club 72, Stack Stars 72]\n[Loop Troop 95, Null Pointers 88, Array Aces 88, Stack Stars 72, Byte Club 72]\n```\n\n`reversed()` turns around the whole comparator it's called on, which is everything to its left. In the first sort, that's just the score, and `thenComparing` after it adds the names, from A to Z. In the second, `reversed()` comes last and turns both criteria around, so the names of a tie go from Z to A. The first one is the scoreboard's rule.\n\n### sorted with a comparator\n\nA stream's `sorted()` takes a comparator too. `sorted()` with nothing in the parentheses uses the natural order (step 1), and `sorted(comparator)` uses the comparator. The scoreboard's top three, as a list of team names:\n\n```java\nimport java.util.ArrayList;\nimport java.util.Comparator;\nimport java.util.List;\nimport java.util.stream.Collectors;\n\npublic class Main {\n    public static void main(String[] args) {\n        List<Entry> entries = new ArrayList<>();\n        entries.add(new Entry(\"Byte Club\", 72));\n        entries.add(new Entry(\"Null Pointers\", 88));\n        entries.add(new Entry(\"Stack Stars\", 72));\n        entries.add(new Entry(\"Loop Troop\", 95));\n        entries.add(new Entry(\"Array Aces\", 88));\n\n        Comparator<Entry> ranking = Comparator.comparing(Entry::getScore).reversed()\n                .thenComparing(Entry::getTeam);\n        List<String> podium = entries.stream()\n                .sorted(ranking)\n                .limit(3)\n                .map(Entry::getTeam)\n                .collect(Collectors.toList());\n        System.out.println(podium);\n        System.out.println(\"First handed in: \" + entries.get(0));\n    }\n}\n// ==== Entry.java ====\npublic class 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    public String getTeam() {\n        return this.team;\n    }\n\n    public int getScore() {\n        return this.score;\n    }\n\n    @Override\n    public String toString() {\n        return this.team + \" \" + this.score;\n    }\n}\n```\n\n```output\n[Loop Troop, Array Aces, Null Pointers]\nFirst handed in: Byte Club 72\n```\n\n- The comparator is kept in a variable, `ranking`, which gives the rule a name. A long chain may go on the next line, like the steps of a stream.\n- `sorted(ranking)` comes before `limit(3)`, so the stream keeps the three best, not the first three handed in (module 37 showed that the order of the steps matters).\n- The stream sorts only what passes through it. The list `entries` keeps the order in which the teams handed in their projects.\n","task":"A tea shop prints its menu with the cheapest teas first, and teas of the same price in alphabetical order. `Tea.java` is ready. In `Main.java`, write the method `sortMenu(List<Tea> teas)`, which sorts the list that way. Use `Comparator.comparing` and `thenComparing`. The check runs:\n\n```java\nList<Tea> menu = new ArrayList<>();\nmenu.add(new Tea(\"Sencha\", 6));\nmenu.add(new Tea(\"Earl Grey\", 4));\nmenu.add(new Tea(\"Oolong\", 6));\nmenu.add(new Tea(\"Chai\", 4));\nmenu.add(new Tea(\"Matcha\", 9));\nsortMenu(menu);\nSystem.out.println(menu);\n```\n\nand expects:\n\n```\n[Chai $4, Earl Grey $4, Oolong $6, Sencha $6, Matcha $9]\n```\n","kind":"code","seed":"// ==== Main.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<Tea> menu = new ArrayList<>();\n        menu.add(new Tea(\"Sencha\", 6));\n        menu.add(new Tea(\"Earl Grey\", 4));\n        menu.add(new Tea(\"Oolong\", 6));\n        menu.add(new Tea(\"Chai\", 4));\n        menu.add(new Tea(\"Matcha\", 9));\n        sortMenu(menu);\n        System.out.println(menu);\n    }\n\n    // Write the method sortMenu here.\n}\n\n// ==== Tea.java ====\npublic class Tea {\n    private String name;\n    private int price;\n\n    public Tea(String name, int price) {\n        this.name = name;\n        this.price = price;\n    }\n\n    public String getName() {\n        return this.name;\n    }\n\n    public int getPrice() {\n        return this.price;\n    }\n\n    @Override\n    public String toString() {\n        return this.name + \" $\" + this.price;\n    }\n}\n","solution":"// ==== Main.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<Tea> menu = new ArrayList<>();\n        menu.add(new Tea(\"Sencha\", 6));\n        menu.add(new Tea(\"Earl Grey\", 4));\n        menu.add(new Tea(\"Oolong\", 6));\n        menu.add(new Tea(\"Chai\", 4));\n        menu.add(new Tea(\"Matcha\", 9));\n        sortMenu(menu);\n        System.out.println(menu);\n    }\n\n    public static void sortMenu(List<Tea> teas) {\n        teas.sort(Comparator.comparing(Tea::getPrice).thenComparing(Tea::getName));\n    }\n}\n\n// ==== Tea.java ====\npublic class Tea {\n    private String name;\n    private int price;\n\n    public Tea(String name, int price) {\n        this.name = name;\n        this.price = price;\n    }\n\n    public String getName() {\n        return this.name;\n    }\n\n    public int getPrice() {\n        return this.price;\n    }\n\n    @Override\n    public String toString() {\n        return this.name + \" $\" + this.price;\n    }\n}\n","hints":["The header is `public static void sortMenu(List<Tea> teas)`, and its body is one call of `teas.sort(...)`.","The price comes first and the name second: `teas.sort(Comparator.comparing(Tea::getPrice).thenComparing(Tea::getName));`."],"tests":[{"name":"Test 1","stdin":"","call":"List<Tea> menu = new ArrayList<>();\nmenu.add(new Tea(\"Sencha\", 6));\nmenu.add(new Tea(\"Earl Grey\", 4));\nmenu.add(new Tea(\"Oolong\", 6));\nmenu.add(new Tea(\"Chai\", 4));\nmenu.add(new Tea(\"Matcha\", 9));\nsortMenu(menu);\nSystem.out.println(menu);\n","expect":"[Chai $4, Earl Grey $4, Oolong $6, Sencha $6, Matcha $9]","hidden":false},{"name":"Test 2","stdin":"","call":"List<Tea> menu = new ArrayList<>();\nsortMenu(menu);\nSystem.out.println(menu);\nmenu.add(new Tea(\"Rooibos\", 5));\nmenu.add(new Tea(\"Mint\", 3));\nmenu.add(new Tea(\"Jasmine\", 5));\nmenu.add(new Tea(\"Assam\", 5));\nmenu.add(new Tea(\"Chamomile\", 3));\nmenu.add(new Tea(\"Darjeeling\", 12));\nsortMenu(menu);\nSystem.out.println(menu);\n","expect":"[]\n[Chamomile $3, Mint $3, Assam $5, Jasmine $5, Rooibos $5, Darjeeling $12]","hidden":true}],"require":[{"pattern":"\\bComparator\\s*\\.\\s*comparing\\s*\\(","message":"Build the comparator with `Comparator.comparing`."},{"pattern":"\\.thenComparing\\s*\\(","message":"Add the second criterion, the name, with `thenComparing`."}],"forbid":[],"more":[{"task":"A warehouse lists its crates by weight, and crates of the same weight by the code of the port they're shipped to. The port codes are Strings, such as `NLRTM` for Rotterdam. What does this program print?\n","kind":"predict","seed":"import java.util.ArrayList;\nimport java.util.Comparator;\nimport java.util.List;\n\npublic class Main {\n    public static void main(String[] args) {\n        List<Crate> crates = new ArrayList<>();\n        crates.add(new Crate(\"ESVLC\", 500));\n        crates.add(new Crate(\"BEANR\", 1200));\n        crates.add(new Crate(\"NLRTM\", 500));\n        crates.add(new Crate(\"DEHAM\", 1200));\n\n        crates.sort(Comparator.comparing(Crate::getKilos).thenComparing(Crate::getPort));\n        System.out.println(crates.get(0) + \", \" + crates.get(1));\n\n        crates.sort(Comparator.comparing(Crate::getKilos).reversed().thenComparing(Crate::getPort));\n        System.out.println(crates.get(0) + \", \" + crates.get(1));\n\n        crates.sort(Comparator.comparing(Crate::getKilos).thenComparing(Crate::getPort).reversed());\n        System.out.println(crates.get(0) + \", \" + crates.get(1));\n    }\n}\n// ==== Crate.java ====\npublic class Crate {\n    private String port;\n    private int kilos;\n\n    public Crate(String port, int kilos) {\n        this.port = port;\n        this.kilos = kilos;\n    }\n\n    public String getPort() {\n        return this.port;\n    }\n\n    public int getKilos() {\n        return this.kilos;\n    }\n\n    @Override\n    public String toString() {\n        return this.port + \" (\" + this.kilos + \" kg)\";\n    }\n}\n","solution":"import java.util.ArrayList;\nimport java.util.Comparator;\nimport java.util.List;\n\npublic class Main {\n    public static void main(String[] args) {\n        List<Crate> crates = new ArrayList<>();\n        crates.add(new Crate(\"ESVLC\", 500));\n        crates.add(new Crate(\"BEANR\", 1200));\n        crates.add(new Crate(\"NLRTM\", 500));\n        crates.add(new Crate(\"DEHAM\", 1200));\n\n        crates.sort(Comparator.comparing(Crate::getKilos).thenComparing(Crate::getPort));\n        System.out.println(crates.get(0) + \", \" + crates.get(1));\n\n        crates.sort(Comparator.comparing(Crate::getKilos).reversed().thenComparing(Crate::getPort));\n        System.out.println(crates.get(0) + \", \" + crates.get(1));\n\n        crates.sort(Comparator.comparing(Crate::getKilos).thenComparing(Crate::getPort).reversed());\n        System.out.println(crates.get(0) + \", \" + crates.get(1));\n    }\n}\n// ==== Crate.java ====\npublic class Crate {\n    private String port;\n    private int kilos;\n\n    public Crate(String port, int kilos) {\n        this.port = port;\n        this.kilos = kilos;\n    }\n\n    public String getPort() {\n        return this.port;\n    }\n\n    public int getKilos() {\n        return this.kilos;\n    }\n\n    @Override\n    public String toString() {\n        return this.port + \" (\" + this.kilos + \" kg)\";\n    }\n}\n","hints":["The first sort puts the light crates first, and the two 500 kg crates by port code, from A to Z: ESVLC before NLRTM.","In the second sort, `reversed()` turns around only the weight, so the heavy crates come first, and the port codes still go from A to Z. In the third, `reversed()` at the end turns around both: the heavy ones first, and the port codes from Z to A."],"tests":[{"name":"Output","stdin":"","expect":"ESVLC (500 kg), NLRTM (500 kg)\nBEANR (1200 kg), DEHAM (1200 kg)\nDEHAM (1200 kg), BEANR (1200 kg)","hidden":false}],"require":[],"forbid":[],"lines":["ESVLC (500 kg), NLRTM (500 kg)","BEANR (1200 kg), DEHAM (1200 kg)","DEHAM (1200 kg), BEANR (1200 kg)"]},{"task":"The airport's app shows the next departures from a given time on. `Flight.java` is the lesson's. In `Main.java`, write the method `nextDepartures(List<Flight> flights, String time, int count)`, which returns a list of the destinations of the first `count` flights that depart at `time` or later, in the order they depart. It shouldn't change the list it's given. Use one stream with `filter`, `sorted` with a comparator, `limit`, `map` and `collect`.\n\nThe times are Strings like `09:05`. A flight departs at `time` or later when `flight.getDeparts().compareTo(time) >= 0`. The check runs:\n\n```java\nList<Flight> flights = new ArrayList<>();\nflights.add(new Flight(\"Lisbon\", \"13:10\", 4));\nflights.add(new Flight(\"Oslo\", \"07:45\", 12));\nflights.add(new Flight(\"Dublin\", \"09:30\", 7));\nflights.add(new Flight(\"Athens\", \"18:20\", 2));\nflights.add(new Flight(\"Riga\", \"09:05\", 12));\nSystem.out.println(nextDepartures(flights, \"09:00\", 3));\nSystem.out.println(nextDepartures(flights, \"12:00\", 5));\n```\n\nand expects:\n\n```\n[Riga, Dublin, Lisbon]\n[Lisbon, Athens]\n```\n","kind":"code","seed":"// ==== Main.java ====\nimport java.util.ArrayList;\nimport java.util.Comparator;\nimport java.util.List;\nimport java.util.stream.Collectors;\n\npublic class Main {\n    public static void main(String[] args) {\n        List<Flight> flights = new ArrayList<>();\n        flights.add(new Flight(\"Lisbon\", \"13:10\", 4));\n        flights.add(new Flight(\"Oslo\", \"07:45\", 12));\n        flights.add(new Flight(\"Dublin\", \"09:30\", 7));\n        flights.add(new Flight(\"Athens\", \"18:20\", 2));\n        flights.add(new Flight(\"Riga\", \"09:05\", 12));\n        System.out.println(nextDepartures(flights, \"09:00\", 3));\n        System.out.println(nextDepartures(flights, \"12:00\", 5));\n    }\n\n    // Write the method nextDepartures here.\n}\n\n// ==== 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","solution":"// ==== Main.java ====\nimport java.util.ArrayList;\nimport java.util.Comparator;\nimport java.util.List;\nimport java.util.stream.Collectors;\n\npublic class Main {\n    public static void main(String[] args) {\n        List<Flight> flights = new ArrayList<>();\n        flights.add(new Flight(\"Lisbon\", \"13:10\", 4));\n        flights.add(new Flight(\"Oslo\", \"07:45\", 12));\n        flights.add(new Flight(\"Dublin\", \"09:30\", 7));\n        flights.add(new Flight(\"Athens\", \"18:20\", 2));\n        flights.add(new Flight(\"Riga\", \"09:05\", 12));\n        System.out.println(nextDepartures(flights, \"09:00\", 3));\n        System.out.println(nextDepartures(flights, \"12:00\", 5));\n    }\n\n    public static List<String> nextDepartures(List<Flight> flights, String time, int count) {\n        return flights.stream()\n                .filter(flight -> flight.getDeparts().compareTo(time) >= 0)\n                .sorted(Comparator.comparing(Flight::getDeparts))\n                .limit(count)\n                .map(Flight::getDestination)\n                .collect(Collectors.toList());\n    }\n}\n\n// ==== 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","hints":["The header is `public static List<String> nextDepartures(List<Flight> flights, String time, int count)`. The filter keeps the flights with `flight.getDeparts().compareTo(time) >= 0`.","Then `.sorted(Comparator.comparing(Flight::getDeparts))`, `.limit(count)`, `.map(Flight::getDestination)` and `.collect(Collectors.toList())`. Sort before `limit`, so that the first flights are the earliest ones."],"tests":[{"name":"Test 1","stdin":"","call":"List<Flight> flights = new ArrayList<>();\nflights.add(new Flight(\"Lisbon\", \"13:10\", 4));\nflights.add(new Flight(\"Oslo\", \"07:45\", 12));\nflights.add(new Flight(\"Dublin\", \"09:30\", 7));\nflights.add(new Flight(\"Athens\", \"18:20\", 2));\nflights.add(new Flight(\"Riga\", \"09:05\", 12));\nSystem.out.println(nextDepartures(flights, \"09:00\", 3));\nSystem.out.println(nextDepartures(flights, \"12:00\", 5));\n","expect":"[Riga, Dublin, Lisbon]\n[Lisbon, Athens]","hidden":false},{"name":"Test 2","stdin":"","call":"List<Flight> flights = new ArrayList<>();\nSystem.out.println(nextDepartures(flights, \"00:00\", 3));\nflights.add(new Flight(\"Vienna\", \"21:40\", 3));\nflights.add(new Flight(\"Porto\", \"06:15\", 1));\nflights.add(new Flight(\"Tallinn\", \"16:05\", 9));\nflights.add(new Flight(\"Bergen\", \"16:50\", 5));\nflights.add(new Flight(\"Split\", \"11:50\", 6));\nSystem.out.println(nextDepartures(flights, \"16:05\", 2));\nSystem.out.println(nextDepartures(flights, \"00:00\", 1));\nSystem.out.println(nextDepartures(flights, \"22:00\", 4));\nSystem.out.println(nextDepartures(flights, \"06:00\", 10));\nSystem.out.println(flights.get(0));\n","expect":"[]\n[Tallinn, Bergen]\n[Porto]\n[]\n[Porto, Split, Tallinn, Bergen, Vienna]\n21:40 Vienna (gate 3)","hidden":true}],"require":[{"pattern":"\\.sorted\\s*\\(\\s*[^)\\s]","message":"Sort the flights in the stream with `sorted`, given a comparator such as `Comparator.comparing(Flight::getDeparts)`."},{"pattern":"\\.limit\\s*\\(","message":"Keep only the first `count` flights with `limit(count)`."}],"forbid":[{"pattern":"\\bCollections\\s*\\.\\s*sort\\b|\\.sort\\s*\\(","message":"`nextDepartures` shouldn't change the list, so let the stream sort with `sorted(...)`."}]}]}