{"id":"streams-why","slug":"why-streams","title":"Why streams: filter and count","text":"Since module 14, you've gone through lists with loops: a for-each loop, an `if` inside it, and a variable that counts or adds up. Java has a second way to do the same work, the **stream**. With a stream, you say *what* should happen to the values, one step after another, and Java does the looping for you.\n\n### One question, two ways\n\nA cycling club keeps the distance of each ride, in kilometers. How many rides were longer than 50 km? Here's the answer with a loop, as you'd write it now:\n\n```java\nimport java.util.ArrayList;\n\npublic class Main {\n    public static void main(String[] args) {\n        ArrayList<Integer> rides = new ArrayList<>();\n        rides.add(42);\n        rides.add(65);\n        rides.add(18);\n        rides.add(80);\n        rides.add(51);\n        rides.add(30);\n\n        int count = 0;\n        for (int km : rides) {\n            if (km > 50) {\n                count++;\n            }\n        }\n        System.out.println(\"Long rides: \" + count);\n    }\n}\n```\n\n```output\nLong rides: 3\n```\n\nAnd here's the same question with a stream:\n\n```java\nimport java.util.ArrayList;\n\npublic class Main {\n    public static void main(String[] args) {\n        ArrayList<Integer> rides = new ArrayList<>();\n        rides.add(42);\n        rides.add(65);\n        rides.add(18);\n        rides.add(80);\n        rides.add(51);\n        rides.add(30);\n\n        long count = rides.stream()\n                .filter(km -> km > 50)\n                .count();\n        System.out.println(\"Long rides: \" + count);\n    }\n}\n```\n\n```output\nLong rides: 3\n```\n\n- `rides.stream()` makes a **stream** of the list's values, in the list's order. A stream isn't a list: it's a flow of values that passes through the steps you chain after it.\n- `.filter(km -> km > 50)` lets through only the values for which the condition is true. `km -> km > 50` reads \"take a value, call it `km`, and answer: is `km` greater than 50?\" It's a **lambda expression**, and step 2 is all about them.\n- `.count()` counts the values that got through, and ends the stream. It returns a `long`, the bigger whole-number type that module 22 mentioned, so the variable is a `long`. It prints just like an `int`.\n\nThe chain is one statement, with one semicolon at the very end. It's split into lines before each dot, so that each line holds one step. Java doesn't mind the line breaks: `rides.stream().filter(km -> km > 50).count()` on one line works the same. The lines after the first are indented a little further, which shows that they continue the statement.\n\n### Read it out loud\n\nThe loop says *how* to find the answer: make a counter, go through the list, check each value, add one. The stream says *what* the answer is: from the rides, keep those over 50 km, and count them. The counter, the loop and the `if` are still there, but inside Java's code, not yours.\n\nBoth versions are right, and both are good Java. This module teaches the stream's tools one at a time, and step 9 comes back to when each way reads better.\n\n### The list stays as it was\n\n`filter` doesn't remove anything from the list. The stream only reads the list's values, and the list is the same afterwards:\n\n```java\nArrayList<Integer> rides = new ArrayList<>();\nrides.add(42);\nrides.add(65);\nrides.add(18);\nlong shortRides = rides.stream()\n        .filter(km -> km < 20)\n        .count();\nSystem.out.println(shortRides);\nSystem.out.println(rides);\n```\n\n```output\n1\n[42, 65, 18]\n```\n\n### Two filters in a row\n\nEach step passes the values it lets through on to the next step. Two filters in a row keep only the values that pass both, just like a condition with `&&`. The rides from 30 to 60 km:\n\n```java\nArrayList<Integer> rides = new ArrayList<>();\nrides.add(42);\nrides.add(65);\nrides.add(18);\nrides.add(80);\nrides.add(51);\nrides.add(30);\nlong middle = rides.stream()\n        .filter(km -> km >= 30)\n        .filter(km -> km <= 60)\n        .count();\nSystem.out.println(middle);\n```\n\n```output\n3\n```\n\n42, 51 and 30 pass both filters. `.filter(km -> km >= 30 && km <= 60)` would give the same count.\n","task":"The club counts the rides of 100 km or more. Fill in the blanks: make a stream of the list, keep the rides of 100 km or more, and count them. The program prints:\n\n```\nRides of 100 km or more: 3\n```\n","kind":"fill","seed":"import java.util.ArrayList;\n\npublic class Main {\n    public static void main(String[] args) {\n        ArrayList<Integer> rides = new ArrayList<>();\n        rides.add(104);\n        rides.add(35);\n        rides.add(120);\n        rides.add(99);\n        rides.add(100);\n        rides.add(47);\n\n        long count = rides.[[stream]]()\n                .[[filter]](km -> km >= 100)\n                .[[count]]();\n        System.out.println(\"Rides of 100 km or more: \" + count);\n    }\n}\n","solution":"import java.util.ArrayList;\n\npublic class Main {\n    public static void main(String[] args) {\n        ArrayList<Integer> rides = new ArrayList<>();\n        rides.add(104);\n        rides.add(35);\n        rides.add(120);\n        rides.add(99);\n        rides.add(100);\n        rides.add(47);\n\n        long count = rides.stream()\n                .filter(km -> km >= 100)\n                .count();\n        System.out.println(\"Rides of 100 km or more: \" + count);\n    }\n}\n","hints":["The chain starts with the method that turns the list into a stream, then lets through only some values, then counts them.","`rides.stream()`, then `.filter(km -> km >= 100)`, then `.count()`."],"tests":[{"name":"Output","stdin":"","expect":"Rides of 100 km or more: 3","hidden":false}],"require":[],"forbid":[],"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> rides = new ArrayList<>();\n        rides.add(25);\n        rides.add(60);\n        rides.add(12);\n        rides.add(60);\n        rides.add(33);\n        rides.add(48);\n\n        long first = rides.stream()\n                .filter(km -> km >= 25)\n                .filter(km -> km < 60)\n                .count();\n        long second = rides.stream()\n                .filter(km -> km == 60)\n                .count();\n        System.out.println(first + \" \" + second);\n        System.out.println(rides.size() + \" \" + rides.get(2));\n    }\n}\n","solution":"import java.util.ArrayList;\n\npublic class Main {\n    public static void main(String[] args) {\n        ArrayList<Integer> rides = new ArrayList<>();\n        rides.add(25);\n        rides.add(60);\n        rides.add(12);\n        rides.add(60);\n        rides.add(33);\n        rides.add(48);\n\n        long first = rides.stream()\n                .filter(km -> km >= 25)\n                .filter(km -> km < 60)\n                .count();\n        long second = rides.stream()\n                .filter(km -> km == 60)\n                .count();\n        System.out.println(first + \" \" + second);\n        System.out.println(rides.size() + \" \" + rides.get(2));\n    }\n}\n","hints":["The first stream keeps the rides of at least 25 km, and of those, the ones under 60 km. The second keeps the rides of exactly 60 km.","25, 33 and 48 pass both filters of the first stream, and 60 is in the list twice. The streams don't change the list: it still has 6 rides, and index 2 is still 12."],"tests":[{"name":"Output","stdin":"","expect":"3 2\n6 12","hidden":false}],"require":[],"forbid":[],"lines":["3 2","6 12"]},{"task":"The club's app counts the short rides, under 20 km, with a loop. Rewrite the body of `shortRides` with a stream: `stream()`, `filter` and `count`, and no loop. Keep the method's header as it is. The check runs:\n\n```java\nArrayList<Integer> rides = new ArrayList<>();\nrides.add(15);\nrides.add(42);\nrides.add(8);\nrides.add(20);\nSystem.out.println(shortRides(rides));\n```\n\nand expects:\n\n```\n2\n```\n","kind":"code","seed":"import java.util.ArrayList;\n\npublic class Main {\n    public static void main(String[] args) {\n        ArrayList<Integer> rides = new ArrayList<>();\n        rides.add(15);\n        rides.add(42);\n        rides.add(8);\n        rides.add(20);\n        System.out.println(shortRides(rides));\n    }\n\n    public static long shortRides(ArrayList<Integer> rides) {\n        long count = 0;\n        for (int km : rides) {\n            if (km < 20) {\n                count++;\n            }\n        }\n        return count;\n    }\n}\n","solution":"import java.util.ArrayList;\n\npublic class Main {\n    public static void main(String[] args) {\n        ArrayList<Integer> rides = new ArrayList<>();\n        rides.add(15);\n        rides.add(42);\n        rides.add(8);\n        rides.add(20);\n        System.out.println(shortRides(rides));\n    }\n\n    public static long shortRides(ArrayList<Integer> rides) {\n        return rides.stream()\n                .filter(km -> km < 20)\n                .count();\n    }\n}\n","hints":["The whole body can be one `return` statement: `return rides.stream()...;`. `count()` already gives a `long`, the method's return type.","`return rides.stream().filter(km -> km < 20).count();`"],"tests":[{"name":"Test 1","stdin":"","call":"ArrayList<Integer> rides = new ArrayList<>();\nrides.add(15);\nrides.add(42);\nrides.add(8);\nrides.add(20);\nSystem.out.println(shortRides(rides));\n","expect":"2","hidden":false},{"name":"Test 2","stdin":"","call":"ArrayList<Integer> none = new ArrayList<>();\nSystem.out.println(shortRides(none));\nArrayList<Integer> rides = new ArrayList<>();\nrides.add(19);\nrides.add(21);\nrides.add(0);\nrides.add(19);\nrides.add(100);\nSystem.out.println(shortRides(rides) + \" \" + rides.size());\n","expect":"0\n3 5","hidden":true}],"require":[{"pattern":"\\.stream\\s*\\(\\s*\\)","message":"Start the chain with `rides.stream()`."},{"pattern":"\\.filter\\s*\\(","message":"Keep the short rides with `filter`."},{"pattern":"\\.count\\s*\\(\\s*\\)","message":"Count the rides that get through with `count()`."}],"forbid":[{"pattern":"\\b(for|while)\\b","message":"Use a stream in place of the loop: no `for` or `while` this time."}]}]}