{"id":"streams-intermediate","slug":"intermediate-operations","title":"Steps in the middle: map, sorted, distinct, limit, skip","text":"`filter` is one intermediate operation. There are more, and each of them takes a stream and gives a new stream, so you can chain as many as you need before the terminal operation. This step's data comes from a cinema: the films it shows today, and the tickets sold for each screening.\n\n### map\n\n`map` turns each value into another one: the lambda's result goes on down the stream in place of the value. A ticket costs 12 dollars, so each screening's takings are its tickets times 12:\n\n```java\nArrayList<Integer> tickets = new ArrayList<>();\ntickets.add(85);\ntickets.add(140);\ntickets.add(32);\ntickets.add(120);\ntickets.stream()\n        .map(sold -> sold * 12)\n        .forEach(System.out::println);\n```\n\n```output\n1020\n1680\n384\n1440\n```\n\nThe new value can be of another type. Here Strings become other Strings, and then numbers:\n\n```java\nArrayList<String> films = new ArrayList<>();\nfilms.add(\"Vertigo\");\nfilms.add(\"Alien\");\nfilms.add(\"Casablanca\");\nfilms.stream()\n        .map(String::toUpperCase)\n        .forEach(System.out::println);\nfilms.stream()\n        .map(film -> film.length())\n        .forEach(System.out::println);\n```\n\n```output\nVERTIGO\nALIEN\nCASABLANCA\n7\n5\n10\n```\n\nStep 3's `mapToInt` is a `map` whose results are `int` values: its stream is an IntStream, with `sum` and `average`. A plain `map` gives a stream of objects, such as Integers or Strings, which has neither.\n\n### sorted and distinct\n\n`sorted()` passes the values on in increasing order: numbers from the smallest, Strings in alphabetical order, as `Collections.sort` sorts them (module 28). `distinct()` lets each value through only once, the first time it arrives, and drops the copies that come later. It compares values with `equals` and `hashCode` (module 32), so it works for Strings and numbers.\n\n```java\nArrayList<Integer> tickets = new ArrayList<>();\ntickets.add(85);\ntickets.add(140);\ntickets.add(32);\ntickets.add(120);\ntickets.stream()\n        .sorted()\n        .forEach(System.out::println);\nArrayList<String> films = new ArrayList<>();\nfilms.add(\"Heat\");\nfilms.add(\"Alien\");\nfilms.add(\"Heat\");\nfilms.add(\"Up\");\nfilms.add(\"Alien\");\nfilms.stream()\n        .distinct()\n        .forEach(System.out::println);\nSystem.out.println(films);\n```\n\n```output\n32\n85\n120\n140\nHeat\nAlien\nUp\n[Heat, Alien, Heat, Up, Alien]\n```\n\nAs always, the list itself keeps its order and its copies: only the stream's values are sorted or dropped.\n\n### limit and skip\n\n`limit(n)` lets through the first `n` values and drops the rest. `skip(n)` drops the first `n` values and lets the rest through. The screenings are in the order of the day, so `limit(2)` gives the first two, and `skip(4)` the ones after the fourth:\n\n```java\nArrayList<Integer> tickets = new ArrayList<>();\ntickets.add(85);\ntickets.add(140);\ntickets.add(32);\ntickets.add(120);\ntickets.add(64);\ntickets.add(150);\ntickets.stream()\n        .limit(2)\n        .forEach(System.out::println);\nSystem.out.println(\"--\");\ntickets.stream()\n        .skip(4)\n        .forEach(System.out::println);\n```\n\n```output\n85\n140\n--\n64\n150\n```\n\nA `limit` bigger than the number of values is fine: all of them get through. A `skip` bigger than that leaves nothing.\n\n### The order of the steps matters\n\nEach step works on what the step before it let through. `sorted()` then `limit(3)` gives the three smallest values; `limit(3)` then `sorted()` gives the first three values, sorted:\n\n```java\nArrayList<Integer> tickets = new ArrayList<>();\ntickets.add(85);\ntickets.add(140);\ntickets.add(32);\ntickets.add(120);\ntickets.add(64);\ntickets.stream()\n        .sorted()\n        .limit(3)\n        .forEach(System.out::println);\nSystem.out.println(\"--\");\ntickets.stream()\n        .limit(3)\n        .sorted()\n        .forEach(System.out::println);\n```\n\n```output\n32\n64\n85\n--\n32\n85\n140\n```\n\nSo read a chain from the top, one step at a time, and ask what each step gets from the one before.\n\n### Nothing happens without a terminal operation\n\nThe intermediate steps only describe what to do. The work starts when a terminal operation asks for the values. This `filter` has a lambda that prints, but the stream has no terminal operation, so the lambda never runs:\n\n```java\nimport java.util.ArrayList;\n\npublic class Main {\n    public static void main(String[] args) {\n        ArrayList<Integer> tickets = new ArrayList<>();\n        tickets.add(85);\n        tickets.add(140);\n        tickets.add(32);\n        tickets.stream().filter(sold -> {\n            System.out.println(\"Checking \" + sold);\n            return sold > 100;\n        });\n        System.out.println(\"Done\");\n    }\n}\n```\n\n```output\nDone\n```\n\nAdd `.count()` after the `)` that closes `filter(...)`, and the lambda runs once for each value. If a stream seems to do nothing, check that it ends with a terminal operation.\n","task":"The cinema's list has one entry per screening, so a film shown three times is in it three times. Write the method `filmsOfTheDay(List<String> screenings)`, which returns a new list of the films, each once, in alphabetical order. Use a stream with `distinct`, `sorted` and `collect(Collectors.toList())`. The check runs:\n\n```java\nList<String> screenings = new ArrayList<>();\nscreenings.add(\"Vertigo\");\nscreenings.add(\"Alien\");\nscreenings.add(\"Heat\");\nscreenings.add(\"Alien\");\nscreenings.add(\"Casablanca\");\nscreenings.add(\"Vertigo\");\nSystem.out.println(filmsOfTheDay(screenings));\n```\n\nand expects:\n\n```\n[Alien, Casablanca, Heat, Vertigo]\n```\n","kind":"code","seed":"import java.util.ArrayList;\nimport java.util.List;\nimport java.util.stream.Collectors;\n\npublic class Main {\n    public static void main(String[] args) {\n        List<String> screenings = new ArrayList<>();\n        screenings.add(\"Vertigo\");\n        screenings.add(\"Alien\");\n        screenings.add(\"Heat\");\n        screenings.add(\"Alien\");\n        screenings.add(\"Casablanca\");\n        screenings.add(\"Vertigo\");\n        System.out.println(filmsOfTheDay(screenings));\n    }\n\n    // Write the method filmsOfTheDay here.\n}\n","solution":"import java.util.ArrayList;\nimport java.util.List;\nimport java.util.stream.Collectors;\n\npublic class Main {\n    public static void main(String[] args) {\n        List<String> screenings = new ArrayList<>();\n        screenings.add(\"Vertigo\");\n        screenings.add(\"Alien\");\n        screenings.add(\"Heat\");\n        screenings.add(\"Alien\");\n        screenings.add(\"Casablanca\");\n        screenings.add(\"Vertigo\");\n        System.out.println(filmsOfTheDay(screenings));\n    }\n\n    public static List<String> filmsOfTheDay(List<String> screenings) {\n        return screenings.stream()\n                .distinct()\n                .sorted()\n                .collect(Collectors.toList());\n    }\n}\n","hints":["The header is `public static List<String> filmsOfTheDay(List<String> screenings)`, and the method returns what the stream collects.","`return screenings.stream().distinct().sorted().collect(Collectors.toList());` (`sorted()` first and `distinct()` after it works too)."],"tests":[{"name":"Test 1","stdin":"","call":"List<String> screenings = new ArrayList<>();\nscreenings.add(\"Vertigo\");\nscreenings.add(\"Alien\");\nscreenings.add(\"Heat\");\nscreenings.add(\"Alien\");\nscreenings.add(\"Casablanca\");\nscreenings.add(\"Vertigo\");\nSystem.out.println(filmsOfTheDay(screenings));\n","expect":"[Alien, Casablanca, Heat, Vertigo]","hidden":false},{"name":"Test 2","stdin":"","call":"List<String> screenings = new ArrayList<>();\nscreenings.add(\"Up\");\nscreenings.add(\"Up\");\nscreenings.add(\"Up\");\nSystem.out.println(filmsOfTheDay(screenings));\nscreenings.add(\"Jaws\");\nscreenings.add(\"Brazil\");\nSystem.out.println(filmsOfTheDay(screenings) + \" \" + screenings.size());\nSystem.out.println(filmsOfTheDay(new ArrayList<>()));\n","expect":"[Up]\n[Brazil, Jaws, Up] 5\n[]","hidden":true}],"require":[{"pattern":"\\bpublic\\s+static\\s+List<String>\\s+filmsOfTheDay\\s*\\(\\s*List<String>\\s+\\w+\\s*\\)","message":"Write the method as `public static List<String> filmsOfTheDay(List<String> screenings)`."},{"pattern":"\\.distinct\\s*\\(\\s*\\)","message":"Drop the copies with `distinct()`."},{"pattern":"\\.sorted\\s*\\(\\s*\\)","message":"Put the films in order with `sorted()`."},{"pattern":"\\.collect\\s*\\(\\s*Collectors\\s*\\.\\s*toList\\s*\\(\\s*\\)\\s*\\)","message":"End the stream with `collect(Collectors.toList())`."}],"forbid":[{"pattern":"\\b(for|while)\\b","message":"Use a stream this time, not a loop."}],"more":[{"task":"What does this program print?\n","kind":"predict","seed":"import java.util.ArrayList;\n\npublic class Main {\n    public static void main(String[] args) {\n        ArrayList<Integer> tickets = new ArrayList<>();\n        tickets.add(90);\n        tickets.add(40);\n        tickets.add(150);\n        tickets.add(40);\n        tickets.add(75);\n\n        tickets.stream()\n                .distinct()\n                .sorted()\n                .skip(1)\n                .limit(2)\n                .forEach(System.out::println);\n        tickets.stream()\n                .limit(3)\n                .map(sold -> sold / 10)\n                .sorted()\n                .forEach(System.out::println);\n    }\n}\n","solution":"import java.util.ArrayList;\n\npublic class Main {\n    public static void main(String[] args) {\n        ArrayList<Integer> tickets = new ArrayList<>();\n        tickets.add(90);\n        tickets.add(40);\n        tickets.add(150);\n        tickets.add(40);\n        tickets.add(75);\n\n        tickets.stream()\n                .distinct()\n                .sorted()\n                .skip(1)\n                .limit(2)\n                .forEach(System.out::println);\n        tickets.stream()\n                .limit(3)\n                .map(sold -> sold / 10)\n                .sorted()\n                .forEach(System.out::println);\n    }\n}\n","hints":["Follow each chain a step at a time. `distinct()` keeps the first copy of each value, `skip(1)` drops the first value that reaches it, and `limit` lets only the first ones through. In the second chain, `sorted()` comes after the division, and dividing `int` values drops the remainder.","`distinct()` leaves 90, 40, 150 and 75, which sort to 40, 75, 90 and 150, so `skip(1)` drops 40 and `limit(2)` keeps 75 and 90. The second chain starts again from the whole list. `limit(3)` keeps 90, 40 and 150, dividing by 10 gives 9, 4 and 15, and only then are they sorted."],"tests":[{"name":"Output","stdin":"","expect":"75\n90\n4\n9\n15","hidden":false}],"require":[],"forbid":[],"lines":["75","90","4","9","15"]},{"task":"The cinema wants to know its quietest screenings. Write the method `quietest(List<Integer> tickets, int n)`, which returns a new list of the `n` smallest ticket counts, from the smallest up. If the list has fewer than `n` values, it returns all of them, sorted. The list it gets must stay as it was. Use a stream. The check runs:\n\n```java\nList<Integer> tickets = new ArrayList<>();\ntickets.add(85);\ntickets.add(140);\ntickets.add(32);\ntickets.add(120);\ntickets.add(64);\nSystem.out.println(quietest(tickets, 2));\nSystem.out.println(tickets);\n```\n\nand expects:\n\n```\n[32, 64]\n[85, 140, 32, 120, 64]\n```\n","kind":"code","seed":"import java.util.ArrayList;\nimport java.util.List;\nimport java.util.stream.Collectors;\n\npublic class Main {\n    public static void main(String[] args) {\n        List<Integer> tickets = new ArrayList<>();\n        tickets.add(85);\n        tickets.add(140);\n        tickets.add(32);\n        tickets.add(120);\n        tickets.add(64);\n        System.out.println(quietest(tickets, 2));\n        System.out.println(tickets);\n    }\n\n    // Write the method quietest here.\n}\n","solution":"import java.util.ArrayList;\nimport java.util.List;\nimport java.util.stream.Collectors;\n\npublic class Main {\n    public static void main(String[] args) {\n        List<Integer> tickets = new ArrayList<>();\n        tickets.add(85);\n        tickets.add(140);\n        tickets.add(32);\n        tickets.add(120);\n        tickets.add(64);\n        System.out.println(quietest(tickets, 2));\n        System.out.println(tickets);\n    }\n\n    public static List<Integer> quietest(List<Integer> tickets, int n) {\n        return tickets.stream()\n                .sorted()\n                .limit(n)\n                .collect(Collectors.toList());\n    }\n}\n","hints":["The header is `public static List<Integer> quietest(List<Integer> tickets, int n)`. Sort first, and then keep the first `n`: the other order keeps the first `n` of the day.","`return tickets.stream().sorted().limit(n).collect(Collectors.toList());` A `limit` bigger than the number of values lets them all through."],"tests":[{"name":"Test 1","stdin":"","call":"List<Integer> tickets = new ArrayList<>();\ntickets.add(85);\ntickets.add(140);\ntickets.add(32);\ntickets.add(120);\ntickets.add(64);\nSystem.out.println(quietest(tickets, 2));\nSystem.out.println(tickets);\n","expect":"[32, 64]\n[85, 140, 32, 120, 64]","hidden":false},{"name":"Test 2","stdin":"","call":"List<Integer> tickets = new ArrayList<>();\ntickets.add(50);\ntickets.add(12);\ntickets.add(50);\ntickets.add(7);\nSystem.out.println(quietest(tickets, 3));\nSystem.out.println(quietest(tickets, 10) + \" \" + quietest(tickets, 0));\nSystem.out.println(tickets);\n","expect":"[7, 12, 50]\n[7, 12, 50, 50] []\n[50, 12, 50, 7]","hidden":true}],"require":[{"pattern":"\\bpublic\\s+static\\s+List<Integer>\\s+quietest\\s*\\(\\s*List<Integer>\\s+\\w+\\s*,\\s*int\\s+\\w+\\s*\\)","message":"Write the method as `public static List<Integer> quietest(List<Integer> tickets, int n)`."},{"pattern":"\\.stream\\s*\\(\\s*\\)","message":"Use a stream: `tickets.stream()`."}],"forbid":[{"pattern":"\\b(for|while)\\b","message":"Use a stream this time, not a loop."},{"pattern":"\\bCollections\\s*\\.\\s*sort\\b|\\.sort\\s*\\(","message":"Let the stream sort with `sorted()`, so the list itself stays as it was."}]}]}