{"id":"useful-techniques-iterators","slug":"iterators","title":"Iterators: removing while going through","text":"Module 14 showed that removing from a list inside a for-each loop stops the program. Here it is once more, with a to-do list that should lose its finished tasks:\n\n```java\nimport java.util.ArrayList;\n\npublic class Main {\n    public static void main(String[] args) {\n        ArrayList<String> tasks = new ArrayList<>();\n        tasks.add(\"buy stamps\");\n        tasks.add(\"DONE call the dentist\");\n        tasks.add(\"water the plants\");\n        tasks.add(\"DONE pay the rent\");\n        for (String task : tasks) {\n            if (task.startsWith(\"DONE\")) {\n                tasks.remove(task);\n            }\n        }\n        System.out.println(tasks);\n    }\n}\n```\n\n```crash\nException in thread \"main\" java.util.ConcurrentModificationException\n\tat Main.main(Main.java:10)\n```\n\nModule 14's fix was an index loop that goes backwards. That works for a list, which has indexes. An **iterator** is Java's general tool for going through a collection one element at a time, and it can remove the element it's on:\n\n```java\nimport java.util.ArrayList;\nimport java.util.Iterator;\n\npublic class Main {\n    public static void main(String[] args) {\n        ArrayList<String> tasks = new ArrayList<>();\n        tasks.add(\"buy stamps\");\n        tasks.add(\"DONE call the dentist\");\n        tasks.add(\"water the plants\");\n        tasks.add(\"DONE pay the rent\");\n\n        Iterator<String> iterator = tasks.iterator();\n        while (iterator.hasNext()) {\n            String task = iterator.next();\n            if (task.startsWith(\"DONE\")) {\n                iterator.remove();\n            }\n        }\n        System.out.println(tasks);\n    }\n}\n```\n\n```output\n[buy stamps, water the plants]\n```\n\n- `tasks.iterator()` returns an `Iterator<String>`, which starts in front of the first element. Iterator is in `java.util`, so it needs `import java.util.Iterator;`.\n- `hasNext()` returns `true` while there are elements left.\n- `next()` moves on to the next element and returns it.\n- `remove()` removes the element that `next()` returned last, from the list itself. The iterator knows about the removal, so it carries on correctly with the next element.\n\n### The rules\n\n- Call `next()` once in each round, after `hasNext()` has said there's an element. A second `next()` in the same round skips an element, and past the end, `next()` stops the program with a NoSuchElementException.\n- Call `remove()` at most once after each `next()`. Before the first `next()`, or twice in a row, it stops the program with an IllegalStateException.\n- Don't change the collection in any other way while the iterator goes through it: only through the iterator's own `remove()`.\n\n### Why a for-each loop can't remove\n\nA for-each loop uses an iterator too, one that you don't see. `tasks.remove(task)` removes behind that iterator's back: the list changes, but the loop's iterator isn't told. When the loop asks it for the next element, the iterator notices that the list has changed and stops the program with a ConcurrentModificationException (\"concurrent\" means at the same time: the list changed while it was being gone through). With the iterator in a variable of your own, you remove through it, and it keeps track.\n\n### Collections without indexes\n\nEvery collection has `iterator()`: lists, sets, and a map's `keySet()` and `values()`. A set has no indexes, so module 14's backwards loop can't go through one, but an iterator can:\n\n```java\nimport java.util.HashSet;\nimport java.util.Iterator;\nimport java.util.Set;\n\npublic class Main {\n    public static void main(String[] args) {\n        Set<String> coupons = new HashSet<>();\n        coupons.add(\"SPRING-10\");\n        coupons.add(\"WINTER-15\");\n        coupons.add(\"SPRING-20\");\n        coupons.add(\"SUMMER-5\");\n\n        Iterator<String> iterator = coupons.iterator();\n        while (iterator.hasNext()) {\n            if (iterator.next().startsWith(\"SPRING\")) {\n                iterator.remove();\n            }\n        }\n        System.out.println(coupons.size() + \" \" + coupons.contains(\"SPRING-10\") + \" \" + coupons.contains(\"SUMMER-5\"));\n    }\n}\n```\n\n```output\n2 false true\n```\n\nHere `next()` is called right in the `if`'s condition. That's fine: it's still called once in each round.\n\nLists and sets also have `removeIf`, which takes a lambda, like module 37's `filter`: `tasks.removeIf(task -> task.startsWith(\"DONE\"));` removes every finished task in one line. An iterator is the tool when the loop does more than remove, for example counts what it removes, adds up the removed values, or stops at the first match.\n","task":"A playlist should lose its short tracks, the ones under 60 seconds. Fill in the blanks: get an iterator from the list, go on while it has elements, take the next one, and remove it when it's short. The program prints:\n\n```\n[214, 187, 302]\n```\n","kind":"fill","seed":"import java.util.ArrayList;\nimport java.util.Iterator;\n\npublic class Main {\n    public static void main(String[] args) {\n        ArrayList<Integer> tracks = new ArrayList<>();\n        tracks.add(214);\n        tracks.add(45);\n        tracks.add(187);\n        tracks.add(30);\n        tracks.add(302);\n\n        Iterator<Integer> iterator = tracks.[[iterator]]();\n        while (iterator.[[hasNext]]()) {\n            int seconds = iterator.[[next]]();\n            if (seconds < 60) {\n                iterator.[[remove]]();\n            }\n        }\n        System.out.println(tracks);\n    }\n}\n","solution":"import java.util.ArrayList;\nimport java.util.Iterator;\n\npublic class Main {\n    public static void main(String[] args) {\n        ArrayList<Integer> tracks = new ArrayList<>();\n        tracks.add(214);\n        tracks.add(45);\n        tracks.add(187);\n        tracks.add(30);\n        tracks.add(302);\n\n        Iterator<Integer> iterator = tracks.iterator();\n        while (iterator.hasNext()) {\n            int seconds = iterator.next();\n            if (seconds < 60) {\n                iterator.remove();\n            }\n        }\n        System.out.println(tracks);\n    }\n}\n","hints":["The four blanks are, in order: the list's method that gives an iterator, and the iterator's methods that ask whether there's more, take the next element, and remove it.","`tracks.iterator()`, `iterator.hasNext()`, `iterator.next()` and `iterator.remove()`."],"tests":[{"name":"Output","stdin":"","expect":"[214, 187, 302]","hidden":false}],"require":[],"forbid":[],"more":[{"task":"What does this program print?\n","kind":"predict","seed":"import java.util.ArrayList;\nimport java.util.Iterator;\n\npublic class Main {\n    public static void main(String[] args) {\n        ArrayList<String> queue = new ArrayList<>();\n        queue.add(\"Ada\");\n        queue.add(\"Ben\");\n        queue.add(\"Cy\");\n        queue.add(\"Dee\");\n        queue.add(\"Eve\");\n\n        Iterator<String> iterator = queue.iterator();\n        int round = 0;\n        while (iterator.hasNext()) {\n            iterator.next();\n            round++;\n            if (round % 2 == 0) {\n                iterator.remove();\n            }\n        }\n        System.out.println(queue + \" \" + round);\n\n        Iterator<String> again = queue.iterator();\n        again.next();\n        again.next();\n        again.remove();\n        System.out.println(queue);\n    }\n}\n","solution":"import java.util.ArrayList;\nimport java.util.Iterator;\n\npublic class Main {\n    public static void main(String[] args) {\n        ArrayList<String> queue = new ArrayList<>();\n        queue.add(\"Ada\");\n        queue.add(\"Ben\");\n        queue.add(\"Cy\");\n        queue.add(\"Dee\");\n        queue.add(\"Eve\");\n\n        Iterator<String> iterator = queue.iterator();\n        int round = 0;\n        while (iterator.hasNext()) {\n            iterator.next();\n            round++;\n            if (round % 2 == 0) {\n                iterator.remove();\n            }\n        }\n        System.out.println(queue + \" \" + round);\n\n        Iterator<String> again = queue.iterator();\n        again.next();\n        again.next();\n        again.remove();\n        System.out.println(queue);\n    }\n}\n","hints":["The first loop goes through all five names, so `round` ends at 5. In rounds 2 and 4, `remove()` removes the name that `next()` returned in that round: Ben and Dee.","The second iterator starts from the front of what's left, Ada, Cy and Eve. After two calls to `next()`, the last one returned is Cy, and that's the one `remove()` removes."],"tests":[{"name":"Output","stdin":"","expect":"[Ada, Cy, Eve] 5\n[Ada, Eve]","hidden":false}],"require":[],"forbid":[],"lines":["[Ada, Cy, Eve] 5","[Ada, Eve]"]},{"task":"A shopping app keeps its coupons in a wallet. `Coupon.java` is ready: a coupon has a code, a discount in percent, and the last day of the month on which it's valid. In `Wallet.java`, write the two missing methods, each with an iterator:\n\n- `removeExpired(int today)`, which removes every coupon whose last day is before `today` (a coupon is still valid on its last day)\n- `use(String code)`, which removes the first coupon with that code (a wallet can hold two with the same code, and capitals count) and returns its discount, or returns 0 if the wallet has no such coupon\n\n`Main.java` is a test drive, and it prints:\n\n```\n[BREAD10 (10%), BOOKS15 (15%)]\n15 0\n[BREAD10 (10%)]\n```\n","kind":"code","seed":"// ==== Wallet.java ====\nimport java.util.ArrayList;\nimport java.util.Iterator;\n\npublic class Wallet {\n    private ArrayList<Coupon> coupons;\n\n    public Wallet() {\n        this.coupons = new ArrayList<>();\n    }\n\n    public void add(Coupon coupon) {\n        this.coupons.add(coupon);\n    }\n\n    public void removeExpired(int today) {\n\n    }\n\n    public int use(String code) {\n        return 0;\n    }\n\n    @Override\n    public String toString() {\n        return this.coupons.toString();\n    }\n}\n\n// ==== Coupon.java ====\npublic class Coupon {\n    private String code;\n    private int percent;\n    private int lastDay;\n\n    public Coupon(String code, int percent, int lastDay) {\n        this.code = code;\n        this.percent = percent;\n        this.lastDay = lastDay;\n    }\n\n    public String getCode() {\n        return this.code;\n    }\n\n    public int getPercent() {\n        return this.percent;\n    }\n\n    public int getLastDay() {\n        return this.lastDay;\n    }\n\n    @Override\n    public String toString() {\n        return this.code + \" (\" + this.percent + \"%)\";\n    }\n}\n\n// ==== Main.java ====\npublic class Main {\n    public static void main(String[] args) {\n        Wallet wallet = new Wallet();\n        wallet.add(new Coupon(\"BREAD10\", 10, 14));\n        wallet.add(new Coupon(\"SHOES25\", 25, 3));\n        wallet.add(new Coupon(\"BOOKS15\", 15, 20));\n        wallet.add(new Coupon(\"TEA5\", 5, 9));\n        wallet.removeExpired(10);\n        System.out.println(wallet);\n        System.out.println(wallet.use(\"BOOKS15\") + \" \" + wallet.use(\"TEA5\"));\n        System.out.println(wallet);\n    }\n}\n","solution":"// ==== Wallet.java ====\nimport java.util.ArrayList;\nimport java.util.Iterator;\n\npublic class Wallet {\n    private ArrayList<Coupon> coupons;\n\n    public Wallet() {\n        this.coupons = new ArrayList<>();\n    }\n\n    public void add(Coupon coupon) {\n        this.coupons.add(coupon);\n    }\n\n    public void removeExpired(int today) {\n        Iterator<Coupon> iterator = this.coupons.iterator();\n        while (iterator.hasNext()) {\n            if (iterator.next().getLastDay() < today) {\n                iterator.remove();\n            }\n        }\n    }\n\n    public int use(String code) {\n        Iterator<Coupon> iterator = this.coupons.iterator();\n        while (iterator.hasNext()) {\n            Coupon coupon = iterator.next();\n            if (coupon.getCode().equals(code)) {\n                iterator.remove();\n                return coupon.getPercent();\n            }\n        }\n        return 0;\n    }\n\n    @Override\n    public String toString() {\n        return this.coupons.toString();\n    }\n}\n\n// ==== Coupon.java ====\npublic class Coupon {\n    private String code;\n    private int percent;\n    private int lastDay;\n\n    public Coupon(String code, int percent, int lastDay) {\n        this.code = code;\n        this.percent = percent;\n        this.lastDay = lastDay;\n    }\n\n    public String getCode() {\n        return this.code;\n    }\n\n    public int getPercent() {\n        return this.percent;\n    }\n\n    public int getLastDay() {\n        return this.lastDay;\n    }\n\n    @Override\n    public String toString() {\n        return this.code + \" (\" + this.percent + \"%)\";\n    }\n}\n\n// ==== Main.java ====\npublic class Main {\n    public static void main(String[] args) {\n        Wallet wallet = new Wallet();\n        wallet.add(new Coupon(\"BREAD10\", 10, 14));\n        wallet.add(new Coupon(\"SHOES25\", 25, 3));\n        wallet.add(new Coupon(\"BOOKS15\", 15, 20));\n        wallet.add(new Coupon(\"TEA5\", 5, 9));\n        wallet.removeExpired(10);\n        System.out.println(wallet);\n        System.out.println(wallet.use(\"BOOKS15\") + \" \" + wallet.use(\"TEA5\"));\n        System.out.println(wallet);\n    }\n}\n","hints":["Both methods start with `Iterator<Coupon> iterator = this.coupons.iterator();` and a `while (iterator.hasNext())` loop. In `removeExpired`, remove the coupon when its `getLastDay()` is smaller than `today`.","In `use`, keep the coupon from `next()` in a variable. When its code `equals` the one asked for, call `iterator.remove()` and return the coupon's percent right away. After the loop, nothing matched: return 0."],"tests":[{"name":"The test drive","stdin":"","expect":"[BREAD10 (10%), BOOKS15 (15%)]\n15 0\n[BREAD10 (10%)]","hidden":false},{"name":"Test 2","stdin":"","call":"Wallet wallet = new Wallet();\nSystem.out.println(wallet.use(\"TEA5\") + \" \" + wallet);\nwallet.removeExpired(31);\nwallet.add(new Coupon(\"A1\", 5, 1));\nwallet.add(new Coupon(\"B2\", 10, 2));\nwallet.add(new Coupon(\"C3\", 15, 3));\nwallet.removeExpired(2);\nSystem.out.println(wallet);\nwallet.removeExpired(4);\nSystem.out.println(wallet + \" \" + wallet.use(\"B2\"));\n","expect":"0 []\n[B2 (10%), C3 (15%)]\n[] 0","hidden":true},{"name":"Test 3","stdin":"","call":"Wallet wallet = new Wallet();\nwallet.add(new Coupon(\"SAME\", 10, 30));\nwallet.add(new Coupon(\"SAME\", 20, 30));\nwallet.add(new Coupon(\"OTHER\", 30, 30));\nSystem.out.println(wallet.use(\"SAME\") + \" \" + wallet);\nSystem.out.println(wallet.use(\"SAME\") + \" \" + wallet.use(\"SAME\") + \" \" + wallet.use(\"other\"));\nwallet.removeExpired(30);\nSystem.out.println(wallet);\n","expect":"10 [SAME (20%), OTHER (30%)]\n20 0 0\n[OTHER (30%)]","hidden":true},{"name":"Test 4","stdin":"","call":"Wallet wallet = new Wallet();\nfor (int day = 1; day <= 3; day++) {\n    wallet.add(new Coupon(\"DAY\" + day, day * 5, 30));\n}\nSystem.out.println(wallet.use(\"DAY2\") + \" \" + wallet);\nSystem.out.println(wallet.use(\"DAY2\") + \" \" + wallet.use(\"DAY3\") + \" \" + wallet);\n","expect":"10 [DAY1 (5%), DAY3 (15%)]\n0 15 [DAY1 (5%)]","hidden":true}],"require":[{"pattern":"\\.iterator\\s*\\(\\s*\\)","min":2,"message":"Go through the coupons with an iterator in both methods: `this.coupons.iterator()`."},{"pattern":"\\.remove\\s*\\(\\s*\\)","min":2,"message":"Remove the coupons with the iterator's own `remove()`, in both methods."}],"forbid":[{"pattern":"\\bremoveIf\\b","message":"`removeIf` would do the loop for you. Use an iterator this time."}]}]}