{"id":"objects-with-lists-removing","slug":"clearing-and-removing","title":"Clearing and removing","text":"An object's list can shrink as well as grow. There are three usual ways: empty the whole list, remove one object, or remove every object that matches a rule.\n\n### Emptying the list\n\nThe list's own `clear()` does it:\n\n```java\npublic void clear() {\n    this.books.clear();\n}\n```\n\nAnother way is to replace the list with a new, empty one: `this.books = new ArrayList<>();`. Either way the shelf is empty afterwards.\n\n### Removing one object\n\nA method can pass an object on to the list's `remove`:\n\n```java\npublic void remove(Book book) {\n    this.books.remove(book);\n}\n```\n\n`remove` looks for the book with `equals` (module 23). Book has no `equals` of its own, so the one every class gets finds only the very same object: the caller must give a book that's on the shelf, not a new Book with the same title. Often it's handier to remove by a value, such as the title. This method removes the first book with that title, and tells whether it found one:\n\n```java\npublic boolean removeTitle(String title) {\n    for (int i = 0; i < this.books.size(); i++) {\n        if (this.books.get(i).getTitle().equals(title)) {\n            this.books.remove(i);\n            return true;\n        }\n    }\n    return false;\n}\n```\n\n`this.books.remove(i)` removes by index, since `i` is an `int` (module 14). The loop never goes on after a removal, because `return true` ends the method right away.\n\n### Removing every object that matches\n\nModule 14 warned about this one: removing inside a for-each loop. It crashes inside a class just as it does in `main`:\n\n```java\npublic class Main {\n    public static void main(String[] args) {\n        Bookshelf shelf = new Bookshelf(\"Attic\");\n        shelf.add(new Book(\"Poems\", \"Dickinson\", 96));\n        shelf.add(new Book(\"Emma\", \"Austen\", 474));\n        shelf.add(new Book(\"Dracula\", \"Stoker\", 418));\n        shelf.removeShorterThan(100);\n        System.out.println(shelf);\n    }\n}\n// ==== Bookshelf.java ====\nimport java.util.ArrayList;\n\npublic class Bookshelf {\n    private String room;\n    private ArrayList<Book> books;\n\n    public Bookshelf(String room) {\n        this.room = room;\n        this.books = new ArrayList<>();\n    }\n\n    public void add(Book book) {\n        this.books.add(book);\n    }\n\n    public void removeShorterThan(int pages) {\n        for (Book book : this.books) {\n            if (book.getPages() < pages) {\n                this.books.remove(book);\n            }\n        }\n    }\n\n    public String toString() {\n        return this.room + \": \" + this.books;\n    }\n}\n// ==== Book.java ====\npublic class Book {\n    private String title;\n    private String author;\n    private int pages;\n\n    public Book(String title, String author, int pages) {\n        this.title = title;\n        this.author = author;\n        this.pages = pages;\n    }\n\n    public int getPages() {\n        return this.pages;\n    }\n\n    public String toString() {\n        return this.title + \" by \" + this.author + \", \" + this.pages + \" pages\";\n    }\n}\n```\n\n```crash\nException in thread \"main\" java.util.ConcurrentModificationException\n\tat Bookshelf.removeShorterThan(Bookshelf.java:17)\n\tat Main.main(Main.java:7)\n```\n\nLine 17 of `Bookshelf.java` is the for-each line: the list changed while the loop was going through it. The fix is the same as in module 14, an index loop that goes backwards:\n\n```java\npublic void removeShorterThan(int pages) {\n    for (int i = this.books.size() - 1; i >= 0; i--) {\n        if (this.books.get(i).getPages() < pages) {\n            this.books.remove(i);\n        }\n    }\n}\n```\n\nGoing from the end, a removal only moves books the loop has already checked. With this version, the attic shelf keeps Emma and Dracula.\n","task":"A fridge keeps a list of foods, and each food knows how many days it has left. `Food.java` is ready, with the getter `getDaysLeft()`. In `Fridge.java`, write the method `throwOutOld()`: it removes every food with less than 1 day left, and keeps the rest in their order. The check runs:\n\n```java\nFridge fridge = new Fridge();\nfridge.put(new Food(\"cheese\", 12));\nfridge.put(new Food(\"milk\", 0));\nfridge.put(new Food(\"yogurt\", -1));\nfridge.put(new Food(\"jam\", 90));\nfridge.throwOutOld();\nSystem.out.println(fridge);\n```\n\nand expects:\n\n```\nFridge: [cheese (12 days), jam (90 days)]\n```\n","kind":"code","seed":"// ==== Fridge.java ====\nimport java.util.ArrayList;\n\npublic class Fridge {\n    private ArrayList<Food> foods;\n\n    public Fridge() {\n        this.foods = new ArrayList<>();\n    }\n\n    public void put(Food food) {\n        this.foods.add(food);\n    }\n\n    // Write the throwOutOld method here.\n\n    public String toString() {\n        return \"Fridge: \" + this.foods;\n    }\n}\n\n// ==== Food.java ====\npublic class Food {\n    private String name;\n    private int daysLeft;\n\n    public Food(String name, int daysLeft) {\n        this.name = name;\n        this.daysLeft = daysLeft;\n    }\n\n    public int getDaysLeft() {\n        return this.daysLeft;\n    }\n\n    public String toString() {\n        return this.name + \" (\" + this.daysLeft + \" days)\";\n    }\n}\n\n// ==== Main.java ====\npublic class Main {\n    public static void main(String[] args) {\n        Fridge fridge = new Fridge();\n        fridge.put(new Food(\"cheese\", 12));\n        fridge.put(new Food(\"milk\", 0));\n        fridge.put(new Food(\"yogurt\", -1));\n        fridge.put(new Food(\"jam\", 90));\n        fridge.throwOutOld();\n        System.out.println(fridge);\n    }\n}\n","solution":"// ==== Fridge.java ====\nimport java.util.ArrayList;\n\npublic class Fridge {\n    private ArrayList<Food> foods;\n\n    public Fridge() {\n        this.foods = new ArrayList<>();\n    }\n\n    public void put(Food food) {\n        this.foods.add(food);\n    }\n\n    public void throwOutOld() {\n        for (int i = this.foods.size() - 1; i >= 0; i--) {\n            if (this.foods.get(i).getDaysLeft() < 1) {\n                this.foods.remove(i);\n            }\n        }\n    }\n\n    public String toString() {\n        return \"Fridge: \" + this.foods;\n    }\n}\n\n// ==== Food.java ====\npublic class Food {\n    private String name;\n    private int daysLeft;\n\n    public Food(String name, int daysLeft) {\n        this.name = name;\n        this.daysLeft = daysLeft;\n    }\n\n    public int getDaysLeft() {\n        return this.daysLeft;\n    }\n\n    public String toString() {\n        return this.name + \" (\" + this.daysLeft + \" days)\";\n    }\n}\n\n// ==== Main.java ====\npublic class Main {\n    public static void main(String[] args) {\n        Fridge fridge = new Fridge();\n        fridge.put(new Food(\"cheese\", 12));\n        fridge.put(new Food(\"milk\", 0));\n        fridge.put(new Food(\"yogurt\", -1));\n        fridge.put(new Food(\"jam\", 90));\n        fridge.throwOutOld();\n        System.out.println(fridge);\n    }\n}\n","hints":["A for-each loop can't remove from the list it goes through. Loop over the indexes backwards: `for (int i = this.foods.size() - 1; i >= 0; i--)`.","In the loop, `this.foods.get(i).getDaysLeft()` is the food's days left. When it's less than 1, `this.foods.remove(i);` removes it by its index."],"tests":[{"name":"Test 1","stdin":"","call":"Fridge fridge = new Fridge();\nfridge.put(new Food(\"cheese\", 12));\nfridge.put(new Food(\"milk\", 0));\nfridge.put(new Food(\"yogurt\", -1));\nfridge.put(new Food(\"jam\", 90));\nfridge.throwOutOld();\nSystem.out.println(fridge);\n","expect":"Fridge: [cheese (12 days), jam (90 days)]","hidden":false},{"name":"Test 2","stdin":"","call":"Fridge fridge = new Fridge();\nfridge.put(new Food(\"fish\", 0));\nfridge.put(new Food(\"soup\", -3));\nfridge.put(new Food(\"rice\", 0));\nfridge.throwOutOld();\nSystem.out.println(fridge);\nfridge.throwOutOld();\nSystem.out.println(fridge);\n","expect":"Fridge: []\nFridge: []","hidden":true},{"name":"Test 3","stdin":"","call":"Fridge fridge = new Fridge();\nfridge.put(new Food(\"eggs\", 1));\nfridge.put(new Food(\"salad\", 2));\nfridge.put(new Food(\"cream\", 0));\nfridge.throwOutOld();\nSystem.out.println(fridge);\n","expect":"Fridge: [eggs (1 days), salad (2 days)]","hidden":true}],"require":[{"pattern":"\\bpublic\\s+void\\s+throwOutOld\\s*\\(\\s*\\)","message":"Write the method as `public void throwOutOld()`."}],"forbid":[],"more":[{"task":"What does this program print? `Toy` has no `equals` of its own, and the toybox's `remove` passes the toy on to the list's `remove`.\n","kind":"predict","seed":"public class Main {\n    public static void main(String[] args) {\n        Toybox box = new Toybox();\n        Toy ball = new Toy(\"ball\");\n        box.add(ball);\n        box.add(new Toy(\"kite\"));\n        box.add(new Toy(\"ball\"));\n        box.remove(new Toy(\"kite\"));\n        System.out.println(box.count());\n        box.remove(ball);\n        System.out.println(box);\n        box.empty();\n        System.out.println(box.count());\n    }\n}\n// ==== Toybox.java ====\nimport java.util.ArrayList;\n\npublic class Toybox {\n    private ArrayList<Toy> toys;\n\n    public Toybox() {\n        this.toys = new ArrayList<>();\n    }\n\n    public void add(Toy toy) {\n        this.toys.add(toy);\n    }\n\n    public void remove(Toy toy) {\n        this.toys.remove(toy);\n    }\n\n    public void empty() {\n        this.toys.clear();\n    }\n\n    public int count() {\n        return this.toys.size();\n    }\n\n    public String toString() {\n        return \"Toys: \" + this.toys;\n    }\n}\n// ==== Toy.java ====\npublic class Toy {\n    private String name;\n\n    public Toy(String name) {\n        this.name = name;\n    }\n\n    public String toString() {\n        return this.name;\n    }\n}\n","solution":"public class Main {\n    public static void main(String[] args) {\n        Toybox box = new Toybox();\n        Toy ball = new Toy(\"ball\");\n        box.add(ball);\n        box.add(new Toy(\"kite\"));\n        box.add(new Toy(\"ball\"));\n        box.remove(new Toy(\"kite\"));\n        System.out.println(box.count());\n        box.remove(ball);\n        System.out.println(box);\n        box.empty();\n        System.out.println(box.count());\n    }\n}\n// ==== Toybox.java ====\nimport java.util.ArrayList;\n\npublic class Toybox {\n    private ArrayList<Toy> toys;\n\n    public Toybox() {\n        this.toys = new ArrayList<>();\n    }\n\n    public void add(Toy toy) {\n        this.toys.add(toy);\n    }\n\n    public void remove(Toy toy) {\n        this.toys.remove(toy);\n    }\n\n    public void empty() {\n        this.toys.clear();\n    }\n\n    public int count() {\n        return this.toys.size();\n    }\n\n    public String toString() {\n        return \"Toys: \" + this.toys;\n    }\n}\n// ==== Toy.java ====\npublic class Toy {\n    private String name;\n\n    public Toy(String name) {\n        this.name = name;\n    }\n\n    public String toString() {\n        return this.name;\n    }\n}\n","hints":["Without its own `equals`, a Toy equals only itself. `new Toy(\"kite\")` in the remove call is a different object from the kite in the box, so nothing is removed.","`box.remove(ball)` gives the very object that `ball` refers to, the first toy in the box, so that one goes. The other ball is a different object and stays. `empty()` clears the list."],"tests":[{"name":"Output","stdin":"","expect":"3\nToys: [kite, ball]\n0","hidden":false}],"require":[],"forbid":[],"lines":["3","Toys: [kite, ball]","0"]},{"task":"A sign-up sheet for a school trip keeps a list of names, and someone may have signed up more than once by mistake. `SignUpSheet.java` has the list, `signUp(String name)` and toString. Write two methods:\n\n- `cancel(String name)` removes every entry of that name, and returns how many it removed.\n- `reset()` empties the sheet.\n\nThe check runs:\n\n```java\nSignUpSheet sheet = new SignUpSheet();\nsheet.signUp(\"Ada\");\nsheet.signUp(\"Ben\");\nsheet.signUp(\"Ada\");\nsheet.signUp(\"Ada\");\nsheet.signUp(\"Cleo\");\nSystem.out.println(sheet.cancel(\"Ada\"));\nSystem.out.println(sheet);\nSystem.out.println(sheet.cancel(\"Zoe\"));\nsheet.reset();\nSystem.out.println(sheet);\n```\n\nand expects:\n\n```\n3\nSigned up: [Ben, Cleo]\n0\nSigned up: []\n```\n","kind":"code","seed":"// ==== SignUpSheet.java ====\nimport java.util.ArrayList;\n\npublic class SignUpSheet {\n    private ArrayList<String> names;\n\n    public SignUpSheet() {\n        this.names = new ArrayList<>();\n    }\n\n    public void signUp(String name) {\n        this.names.add(name);\n    }\n\n    // Write the methods cancel and reset here.\n\n    public String toString() {\n        return \"Signed up: \" + this.names;\n    }\n}\n\n// ==== Main.java ====\npublic class Main {\n    public static void main(String[] args) {\n        SignUpSheet sheet = new SignUpSheet();\n        sheet.signUp(\"Ada\");\n        sheet.signUp(\"Ben\");\n        sheet.signUp(\"Ada\");\n        sheet.signUp(\"Ada\");\n        sheet.signUp(\"Cleo\");\n        System.out.println(sheet.cancel(\"Ada\"));\n        System.out.println(sheet);\n        System.out.println(sheet.cancel(\"Zoe\"));\n        sheet.reset();\n        System.out.println(sheet);\n    }\n}\n","solution":"// ==== SignUpSheet.java ====\nimport java.util.ArrayList;\n\npublic class SignUpSheet {\n    private ArrayList<String> names;\n\n    public SignUpSheet() {\n        this.names = new ArrayList<>();\n    }\n\n    public void signUp(String name) {\n        this.names.add(name);\n    }\n\n    public int cancel(String name) {\n        int removed = 0;\n        for (int i = this.names.size() - 1; i >= 0; i--) {\n            if (this.names.get(i).equals(name)) {\n                this.names.remove(i);\n                removed++;\n            }\n        }\n        return removed;\n    }\n\n    public void reset() {\n        this.names.clear();\n    }\n\n    public String toString() {\n        return \"Signed up: \" + this.names;\n    }\n}\n\n// ==== Main.java ====\npublic class Main {\n    public static void main(String[] args) {\n        SignUpSheet sheet = new SignUpSheet();\n        sheet.signUp(\"Ada\");\n        sheet.signUp(\"Ben\");\n        sheet.signUp(\"Ada\");\n        sheet.signUp(\"Ada\");\n        sheet.signUp(\"Cleo\");\n        System.out.println(sheet.cancel(\"Ada\"));\n        System.out.println(sheet);\n        System.out.println(sheet.cancel(\"Zoe\"));\n        sheet.reset();\n        System.out.println(sheet);\n    }\n}\n","hints":["`this.names.remove(name)` removes only the first copy. To remove them all and count them, loop over the indexes backwards, and when `this.names.get(i).equals(name)`, remove index `i` and add one to a counter.","`reset` needs one line: `this.names.clear();`."],"tests":[{"name":"Test 1","stdin":"","call":"SignUpSheet sheet = new SignUpSheet();\nsheet.signUp(\"Ada\");\nsheet.signUp(\"Ben\");\nsheet.signUp(\"Ada\");\nsheet.signUp(\"Ada\");\nsheet.signUp(\"Cleo\");\nSystem.out.println(sheet.cancel(\"Ada\"));\nSystem.out.println(sheet);\nSystem.out.println(sheet.cancel(\"Zoe\"));\nsheet.reset();\nSystem.out.println(sheet);\n","expect":"3\nSigned up: [Ben, Cleo]\n0\nSigned up: []","hidden":false},{"name":"Test 2","stdin":"","call":"SignUpSheet sheet = new SignUpSheet();\nsheet.signUp(\"Kai\");\nsheet.signUp(\"Kai\");\nsheet.signUp(\"Lu\");\nString name = \"K\";\nSystem.out.println(sheet.cancel(name + \"ai\") + \" \" + sheet.cancel(\"lu\"));\nSystem.out.println(sheet);\nsheet.signUp(\"Mo\");\nSystem.out.println(sheet.cancel(\"Lu\") + \" \" + sheet);\n","expect":"2 0\nSigned up: [Lu]\n1 Signed up: [Mo]","hidden":true},{"name":"Test 3","stdin":"","call":"SignUpSheet sheet = new SignUpSheet();\nsheet.reset();\nSystem.out.println(sheet.cancel(\"Ada\"));\nsheet.signUp(\"Noa\");\nsheet.signUp(\"Noa\");\nsheet.signUp(\"Noa\");\nSystem.out.println(sheet.cancel(\"Noa\") + \" \" + sheet);\n","expect":"0\n3 Signed up: []","hidden":true}],"require":[{"pattern":"\\bpublic\\s+int\\s+cancel\\s*\\(\\s*String\\s+\\w+\\s*\\)","message":"Write the method as `public int cancel(String name)`."},{"pattern":"\\bpublic\\s+void\\s+reset\\s*\\(\\s*\\)","message":"Write the method as `public void reset()`."}],"forbid":[]}]}