{"id":"objects-with-lists-rules","slug":"rules-for-adding","title":"Rules the owner enforces","text":"So far every `add` put its object on the list, no questions asked. But the object that owns the list can also have rules about what goes on it. A parking garage has a fixed number of spots, and the same car can't be parked in it twice:\n\n```java\npublic class Main {\n    public static void main(String[] args) {\n        ParkingGarage garage = new ParkingGarage(2);\n        System.out.println(garage.park(new Car(\"ABC-123\")));\n        System.out.println(garage.park(new Car(\"ABC-123\")));\n        System.out.println(garage.park(new Car(\"XYZ-789\")));\n        System.out.println(garage.park(new Car(\"JKL-456\")));\n        garage.leave(\"ABC-123\");\n        System.out.println(garage.park(new Car(\"JKL-456\")));\n        System.out.println(garage);\n    }\n}\n// ==== ParkingGarage.java ====\nimport java.util.ArrayList;\n\npublic class ParkingGarage {\n    private int spots;\n    private ArrayList<Car> cars;\n\n    public ParkingGarage(int spots) {\n        this.spots = spots;\n        this.cars = new ArrayList<>();\n    }\n\n    public boolean park(Car car) {\n        if (this.cars.size() >= this.spots) {\n            return false;\n        }\n        if (this.isParked(car.getPlate())) {\n            return false;\n        }\n        this.cars.add(car);\n        return true;\n    }\n\n    public boolean isParked(String plate) {\n        for (Car car : this.cars) {\n            if (car.getPlate().equals(plate)) {\n                return true;\n            }\n        }\n        return false;\n    }\n\n    public void leave(String plate) {\n        for (int i = this.cars.size() - 1; i >= 0; i--) {\n            if (this.cars.get(i).getPlate().equals(plate)) {\n                this.cars.remove(i);\n            }\n        }\n    }\n\n    public String toString() {\n        return this.cars.size() + \"/\" + this.spots + \" spots taken: \" + this.cars;\n    }\n}\n// ==== Car.java ====\npublic class Car {\n    private String plate;\n\n    public Car(String plate) {\n        this.plate = plate;\n    }\n\n    public String getPlate() {\n        return this.plate;\n    }\n\n    public String toString() {\n        return this.plate;\n    }\n}\n```\n\n```output\ntrue\nfalse\ntrue\nfalse\ntrue\n2/2 spots taken: [XYZ-789, JKL-456]\n```\n\n- `park` checks its rules first, and adds the car only when all of them hold: there's a free spot, and no car with the same plate is parked. Each rule that fails returns `false` right away.\n- The `boolean` it returns tells the caller whether the car got in. `main` prints it here; a real program could print `Garage full` instead.\n- `park` uses another method of the same object, `this.isParked(...)`. Small methods like this one can be useful to the caller too: `garage.isParked(\"ABC-123\")`.\n- After `leave`, there's a free spot again, and the car that was turned away gets in.\n\nThe rules always hold because the list is private. The only way onto the list is through `park`, and `park` checks every car. No other part of the program can put a car in past the rules, however it's written.\n\n### Don't hand out the list\n\nSuppose ParkingGarage had a getter for its list:\n\n```java\npublic ArrayList<Car> getCars() {\n    return this.cars;\n}\n```\n\nIt returns a reference to the garage's own list, not a copy (module 22). With it, `garage.getCars().add(new Car(\"NEW-001\"));` puts a car straight on the list, past every rule, even into a full garage. So keep the list to the class, and give the class methods for what the rest of the program needs: `park`, `leave`, `isParked`, a toString.\n\n### The module's tools\n\n| Tool | Example |\n| --- | --- |\n| A list as an instance variable | `private ArrayList<Book> books;` |\n| Made in the constructor | `this.books = new ArrayList<>();` |\n| A loop over the object's list | `for (Book book : this.books)` |\n| A toString built in a loop | `text = text + \"\\n\" + book;` |\n| Removing while going through | an index loop that goes backwards |\n| A total from the objects | `total = total + book.getPages();` |\n| A search that may find nothing | returns the object, or `null` |\n| A rule checked before adding | `if (this.cars.size() >= this.spots)` |\n","task":"A day plan holds meetings, and two meetings can't be at the same time. A meeting has a title, and a start and an end hour: `new Meeting(\"Review\", 10, 12)` is from 10 to 12. `Meeting.java` is ready, with the getters `getStart()` and `getEnd()`. In `DayPlan.java`, write the method `add(Meeting meeting)`. It adds the meeting only when it doesn't overlap any meeting already on the plan, and returns `true` when the meeting was added, `false` when it wasn't.\n\nTwo meetings overlap when each one starts before the other one ends. A meeting from 9 to 10 and one from 10 to 12 don't overlap: the first ends just as the second starts. The check runs:\n\n```java\nDayPlan plan = new DayPlan();\nSystem.out.println(plan.add(new Meeting(\"Standup\", 9, 10)));\nSystem.out.println(plan.add(new Meeting(\"Review\", 10, 12)));\nSystem.out.println(plan.add(new Meeting(\"Lunch talk\", 11, 13)));\nSystem.out.println(plan.add(new Meeting(\"Call\", 8, 9)));\nSystem.out.println(plan);\n```\n\nand expects:\n\n```\ntrue\ntrue\nfalse\ntrue\nBooked: [Standup (9 to 10), Review (10 to 12), Call (8 to 9)]\n```\n","kind":"code","seed":"// ==== DayPlan.java ====\nimport java.util.ArrayList;\n\npublic class DayPlan {\n    private ArrayList<Meeting> meetings;\n\n    public DayPlan() {\n        this.meetings = new ArrayList<>();\n    }\n\n    // Write the add method here.\n\n    public String toString() {\n        return \"Booked: \" + this.meetings;\n    }\n}\n\n// ==== Meeting.java ====\npublic class Meeting {\n    private String title;\n    private int start;\n    private int end;\n\n    public Meeting(String title, int start, int end) {\n        this.title = title;\n        this.start = start;\n        this.end = end;\n    }\n\n    public int getStart() {\n        return this.start;\n    }\n\n    public int getEnd() {\n        return this.end;\n    }\n\n    public String toString() {\n        return this.title + \" (\" + this.start + \" to \" + this.end + \")\";\n    }\n}\n\n// ==== Main.java ====\npublic class Main {\n    public static void main(String[] args) {\n        DayPlan plan = new DayPlan();\n        System.out.println(plan.add(new Meeting(\"Standup\", 9, 10)));\n        System.out.println(plan.add(new Meeting(\"Review\", 10, 12)));\n        System.out.println(plan.add(new Meeting(\"Lunch talk\", 11, 13)));\n        System.out.println(plan.add(new Meeting(\"Call\", 8, 9)));\n        System.out.println(plan);\n    }\n}\n","solution":"// ==== DayPlan.java ====\nimport java.util.ArrayList;\n\npublic class DayPlan {\n    private ArrayList<Meeting> meetings;\n\n    public DayPlan() {\n        this.meetings = new ArrayList<>();\n    }\n\n    public boolean add(Meeting meeting) {\n        for (Meeting other : this.meetings) {\n            if (meeting.getStart() < other.getEnd() && other.getStart() < meeting.getEnd()) {\n                return false;\n            }\n        }\n        this.meetings.add(meeting);\n        return true;\n    }\n\n    public String toString() {\n        return \"Booked: \" + this.meetings;\n    }\n}\n\n// ==== Meeting.java ====\npublic class Meeting {\n    private String title;\n    private int start;\n    private int end;\n\n    public Meeting(String title, int start, int end) {\n        this.title = title;\n        this.start = start;\n        this.end = end;\n    }\n\n    public int getStart() {\n        return this.start;\n    }\n\n    public int getEnd() {\n        return this.end;\n    }\n\n    public String toString() {\n        return this.title + \" (\" + this.start + \" to \" + this.end + \")\";\n    }\n}\n\n// ==== Main.java ====\npublic class Main {\n    public static void main(String[] args) {\n        DayPlan plan = new DayPlan();\n        System.out.println(plan.add(new Meeting(\"Standup\", 9, 10)));\n        System.out.println(plan.add(new Meeting(\"Review\", 10, 12)));\n        System.out.println(plan.add(new Meeting(\"Lunch talk\", 11, 13)));\n        System.out.println(plan.add(new Meeting(\"Call\", 8, 9)));\n        System.out.println(plan);\n    }\n}\n","hints":["Check the rule before adding: go through the meetings already booked, and return `false` as soon as one of them overlaps the new one. Only after the loop, when none did, add the meeting and return `true`.","In `for (Meeting other : this.meetings)`, the two overlap when `meeting.getStart() < other.getEnd() && other.getStart() < meeting.getEnd()`. With `<`, a meeting that starts just as another ends still fits."],"tests":[{"name":"Test 1","stdin":"","call":"DayPlan plan = new DayPlan();\nSystem.out.println(plan.add(new Meeting(\"Standup\", 9, 10)));\nSystem.out.println(plan.add(new Meeting(\"Review\", 10, 12)));\nSystem.out.println(plan.add(new Meeting(\"Lunch talk\", 11, 13)));\nSystem.out.println(plan.add(new Meeting(\"Call\", 8, 9)));\nSystem.out.println(plan);\n","expect":"true\ntrue\nfalse\ntrue\nBooked: [Standup (9 to 10), Review (10 to 12), Call (8 to 9)]","hidden":false},{"name":"Test 2","stdin":"","call":"DayPlan plan = new DayPlan();\nSystem.out.println(plan);\nSystem.out.println(plan.add(new Meeting(\"Workshop\", 9, 12)));\nSystem.out.println(plan.add(new Meeting(\"Chat\", 10, 11)));\nSystem.out.println(plan.add(new Meeting(\"All day\", 8, 18)));\nSystem.out.println(plan.add(new Meeting(\"Workshop again\", 9, 12)));\nSystem.out.println(plan.add(new Meeting(\"Early\", 7, 9)));\nSystem.out.println(plan);\n","expect":"Booked: []\ntrue\nfalse\nfalse\nfalse\ntrue\nBooked: [Workshop (9 to 12), Early (7 to 9)]","hidden":true},{"name":"Test 3","stdin":"","call":"DayPlan plan = new DayPlan();\nSystem.out.println(plan.add(new Meeting(\"Late\", 16, 17)));\nSystem.out.println(plan.add(new Meeting(\"Before\", 14, 16)));\nSystem.out.println(plan.add(new Meeting(\"Across\", 15, 17)));\nSystem.out.println(plan.add(new Meeting(\"After\", 17, 18)));\nSystem.out.println(plan);\n","expect":"true\ntrue\nfalse\ntrue\nBooked: [Late (16 to 17), Before (14 to 16), After (17 to 18)]","hidden":true}],"require":[{"pattern":"\\bpublic\\s+boolean\\s+add\\s*\\(\\s*Meeting\\s+\\w+\\s*\\)","message":"Write the method as `public boolean add(Meeting meeting)`."},{"pattern":"\\.getStart\\s*\\(\\s*\\)","message":"Ask the meetings when they start with `getStart()`."},{"pattern":"\\.getEnd\\s*\\(\\s*\\)","message":"Ask the meetings when they end with `getEnd()`."}],"forbid":[],"more":[{"task":"What does this program print? The dinner table lets a guest sit only when there's a free chair, but it also has a getter that returns its list.\n","kind":"predict","seed":"import java.util.ArrayList;\n\npublic class Main {\n    public static void main(String[] args) {\n        DinnerTable table = new DinnerTable(2);\n        System.out.println(table.seat(\"Ada\"));\n        System.out.println(table.seat(\"Ben\"));\n        System.out.println(table.seat(\"Cleo\"));\n        ArrayList<String> guests = table.getGuests();\n        guests.add(\"Dan\");\n        System.out.println(table);\n    }\n}\n// ==== DinnerTable.java ====\nimport java.util.ArrayList;\n\npublic class DinnerTable {\n    private int chairs;\n    private ArrayList<String> guests;\n\n    public DinnerTable(int chairs) {\n        this.chairs = chairs;\n        this.guests = new ArrayList<>();\n    }\n\n    public boolean seat(String guest) {\n        if (this.guests.size() >= this.chairs) {\n            return false;\n        }\n        this.guests.add(guest);\n        return true;\n    }\n\n    public ArrayList<String> getGuests() {\n        return this.guests;\n    }\n\n    public String toString() {\n        return this.guests.size() + \" guests, \" + this.chairs + \" chairs: \" + this.guests;\n    }\n}\n","solution":"import java.util.ArrayList;\n\npublic class Main {\n    public static void main(String[] args) {\n        DinnerTable table = new DinnerTable(2);\n        System.out.println(table.seat(\"Ada\"));\n        System.out.println(table.seat(\"Ben\"));\n        System.out.println(table.seat(\"Cleo\"));\n        ArrayList<String> guests = table.getGuests();\n        guests.add(\"Dan\");\n        System.out.println(table);\n    }\n}\n// ==== DinnerTable.java ====\nimport java.util.ArrayList;\n\npublic class DinnerTable {\n    private int chairs;\n    private ArrayList<String> guests;\n\n    public DinnerTable(int chairs) {\n        this.chairs = chairs;\n        this.guests = new ArrayList<>();\n    }\n\n    public boolean seat(String guest) {\n        if (this.guests.size() >= this.chairs) {\n            return false;\n        }\n        this.guests.add(guest);\n        return true;\n    }\n\n    public ArrayList<String> getGuests() {\n        return this.guests;\n    }\n\n    public String toString() {\n        return this.guests.size() + \" guests, \" + this.chairs + \" chairs: \" + this.guests;\n    }\n}\n","hints":["`seat` checks the chairs: Ada and Ben get the two chairs, and Cleo is turned away.","`getGuests()` returns a reference to the table's own list, not a copy. So `guests.add(\"Dan\")` adds Dan to the table's list, and `seat`'s rule is never asked."],"tests":[{"name":"Output","stdin":"","expect":"true\ntrue\nfalse\n3 guests, 2 chairs: [Ada, Ben, Dan]","hidden":false}],"require":[],"forbid":[],"lines":["true","true","false","3 guests, 2 chairs: [Ada, Ben, Dan]"]},{"task":"A five-a-side team keeps a roster of its players, and each player has a shirt number. `Player.java` is ready, with `getName()` and `getNumber()`, and a toString like `#7 Ada`. `Roster.java` has the team's name, the most players it may have, and the list. Write three methods in it:\n\n- `add(Player player)` adds the player and returns `true`, except when the roster is already full, or when a player on it already has the same shirt number. Then it adds nothing and returns `false`.\n- `remove(int number)` removes the player with that shirt number and returns `true`, or returns `false` when there's no such player.\n- `toString()` gives the team's name and how many players it has of the most it may have, like `Otters: 2/3 players`, and then each player on a line of its own.\n\nThe check runs:\n\n```java\nRoster roster = new Roster(\"Otters\", 3);\nSystem.out.println(roster.add(new Player(\"Ada\", 7)));\nSystem.out.println(roster.add(new Player(\"Ben\", 7)));\nSystem.out.println(roster.add(new Player(\"Ben\", 9)));\nSystem.out.println(roster.add(new Player(\"Cleo\", 4)));\nSystem.out.println(roster.add(new Player(\"Dan\", 11)));\nSystem.out.println(roster.remove(9));\nSystem.out.println(roster.remove(5));\nSystem.out.println(roster);\n```\n\nand expects:\n\n```\ntrue\nfalse\ntrue\ntrue\nfalse\ntrue\nfalse\nOtters: 2/3 players\n#7 Ada\n#4 Cleo\n```\n","kind":"code","seed":"// ==== Roster.java ====\nimport java.util.ArrayList;\n\npublic class Roster {\n    private String team;\n    private int maxPlayers;\n    private ArrayList<Player> players;\n\n    public Roster(String team, int maxPlayers) {\n        this.team = team;\n        this.maxPlayers = maxPlayers;\n        this.players = new ArrayList<>();\n    }\n\n    // Write the methods add, remove and toString here.\n}\n\n// ==== Player.java ====\npublic class Player {\n    private String name;\n    private int number;\n\n    public Player(String name, int number) {\n        this.name = name;\n        this.number = number;\n    }\n\n    public String getName() {\n        return this.name;\n    }\n\n    public int getNumber() {\n        return this.number;\n    }\n\n    public String toString() {\n        return \"#\" + this.number + \" \" + this.name;\n    }\n}\n\n// ==== Main.java ====\npublic class Main {\n    public static void main(String[] args) {\n        Roster roster = new Roster(\"Otters\", 3);\n        System.out.println(roster.add(new Player(\"Ada\", 7)));\n        System.out.println(roster.add(new Player(\"Ben\", 7)));\n        System.out.println(roster.add(new Player(\"Ben\", 9)));\n        System.out.println(roster.add(new Player(\"Cleo\", 4)));\n        System.out.println(roster.add(new Player(\"Dan\", 11)));\n        System.out.println(roster.remove(9));\n        System.out.println(roster.remove(5));\n        System.out.println(roster);\n    }\n}\n","solution":"// ==== Roster.java ====\nimport java.util.ArrayList;\n\npublic class Roster {\n    private String team;\n    private int maxPlayers;\n    private ArrayList<Player> players;\n\n    public Roster(String team, int maxPlayers) {\n        this.team = team;\n        this.maxPlayers = maxPlayers;\n        this.players = new ArrayList<>();\n    }\n\n    public boolean add(Player player) {\n        if (this.players.size() >= this.maxPlayers) {\n            return false;\n        }\n        for (Player other : this.players) {\n            if (other.getNumber() == player.getNumber()) {\n                return false;\n            }\n        }\n        this.players.add(player);\n        return true;\n    }\n\n    public boolean remove(int number) {\n        for (int i = 0; i < this.players.size(); i++) {\n            if (this.players.get(i).getNumber() == number) {\n                this.players.remove(i);\n                return true;\n            }\n        }\n        return false;\n    }\n\n    public String toString() {\n        String text = this.team + \": \" + this.players.size() + \"/\" + this.maxPlayers + \" players\";\n        for (Player player : this.players) {\n            text = text + \"\\n\" + player;\n        }\n        return text;\n    }\n}\n\n// ==== Player.java ====\npublic class Player {\n    private String name;\n    private int number;\n\n    public Player(String name, int number) {\n        this.name = name;\n        this.number = number;\n    }\n\n    public String getName() {\n        return this.name;\n    }\n\n    public int getNumber() {\n        return this.number;\n    }\n\n    public String toString() {\n        return \"#\" + this.number + \" \" + this.name;\n    }\n}\n\n// ==== Main.java ====\npublic class Main {\n    public static void main(String[] args) {\n        Roster roster = new Roster(\"Otters\", 3);\n        System.out.println(roster.add(new Player(\"Ada\", 7)));\n        System.out.println(roster.add(new Player(\"Ben\", 7)));\n        System.out.println(roster.add(new Player(\"Ben\", 9)));\n        System.out.println(roster.add(new Player(\"Cleo\", 4)));\n        System.out.println(roster.add(new Player(\"Dan\", 11)));\n        System.out.println(roster.remove(9));\n        System.out.println(roster.remove(5));\n        System.out.println(roster);\n    }\n}\n","hints":["`add` checks two rules before it adds: `this.players.size() >= this.maxPlayers` means full, and a loop over the players looks for one whose `getNumber()` is the new player's number. Either one returns `false`. Shirt numbers are `int` values, so `==` compares them.","`remove` loops over the indexes, and when `this.players.get(i).getNumber() == number`, removes index `i` and returns `true` at once. For toString, start with the first line and add `\"\\n\" + player` for each player, as in step 5."],"tests":[{"name":"Test 1","stdin":"","call":"Roster roster = new Roster(\"Otters\", 3);\nSystem.out.println(roster.add(new Player(\"Ada\", 7)));\nSystem.out.println(roster.add(new Player(\"Ben\", 7)));\nSystem.out.println(roster.add(new Player(\"Ben\", 9)));\nSystem.out.println(roster.add(new Player(\"Cleo\", 4)));\nSystem.out.println(roster.add(new Player(\"Dan\", 11)));\nSystem.out.println(roster.remove(9));\nSystem.out.println(roster.remove(5));\nSystem.out.println(roster);\n","expect":"true\nfalse\ntrue\ntrue\nfalse\ntrue\nfalse\nOtters: 2/3 players\n#7 Ada\n#4 Cleo","hidden":false},{"name":"Test 2","stdin":"","call":"Roster roster = new Roster(\"Lynx\", 5);\nSystem.out.println(roster);\nSystem.out.println(roster.remove(1));\nSystem.out.println(roster.add(new Player(\"Eva\", 1)) + \" \" + roster.add(new Player(\"Eva\", 2)) + \" \" + roster.add(new Player(\"Fin\", 1)));\nSystem.out.println(roster);\n","expect":"Lynx: 0/5 players\nfalse\ntrue true false\nLynx: 2/5 players\n#1 Eva\n#2 Eva","hidden":true},{"name":"Test 3","stdin":"","call":"Roster roster = new Roster(\"Crows\", 2);\nroster.add(new Player(\"Gus\", 3));\nroster.add(new Player(\"Hal\", 5));\nSystem.out.println(roster.add(new Player(\"Ivy\", 8)));\nSystem.out.println(roster.remove(3));\nSystem.out.println(roster.add(new Player(\"Ivy\", 8)));\nSystem.out.println(roster.add(new Player(\"Jo\", 3)));\nSystem.out.println(roster);\n","expect":"false\ntrue\ntrue\nfalse\nCrows: 2/2 players\n#5 Hal\n#8 Ivy","hidden":true},{"name":"Test 4","stdin":"","call":"Roster roster = new Roster(\"Moles\", 4);\nroster.add(new Player(\"Kit\", 2));\nroster.add(new Player(\"Lev\", 6));\nroster.add(new Player(\"Max\", 9));\nSystem.out.println(roster.remove(6) + \" \" + roster.remove(6));\nSystem.out.println(roster);\n","expect":"true false\nMoles: 2/4 players\n#2 Kit\n#9 Max","hidden":true}],"require":[{"pattern":"\\bpublic\\s+boolean\\s+add\\s*\\(\\s*Player\\s+\\w+\\s*\\)","message":"Write the method as `public boolean add(Player player)`."},{"pattern":"\\bpublic\\s+boolean\\s+remove\\s*\\(\\s*int\\s+\\w+\\s*\\)","message":"Write the method as `public boolean remove(int number)`."},{"pattern":"\\bpublic\\s+String\\s+toString\\s*\\(\\s*\\)","min":2,"message":"Write the method in Roster as `public String toString()`."}],"forbid":[]}]}