{"id":"list-and-map-internals-own-list","slug":"own-list","title":"A list of your own","text":"Step 1 kept an array, a count and the copying in `main`. A list keeps all three inside an object, and the code that uses it only calls `add`. Here's a Playlist class that keeps song titles that way:\n\n```java\npublic class Main {\n    public static void main(String[] args) {\n        Playlist playlist = new Playlist();\n        String[] songs = {\"Morning Rain\", \"Night Train\", \"Paper Boats\", \"Slow River\", \"Copper Sky\", \"Lantern Light\"};\n        for (String song : songs) {\n            playlist.add(song);\n            System.out.println(\"size \" + playlist.size() + \", array length \" + playlist.capacity());\n        }\n        System.out.println(playlist);\n    }\n}\n// ==== Playlist.java ====\npublic class Playlist {\n    private String[] songs;\n    private int size;\n\n    public Playlist() {\n        this.songs = new String[4];\n        this.size = 0;\n    }\n\n    public void add(String song) {\n        if (this.size == this.songs.length) {\n            grow();\n        }\n        this.songs[this.size] = song;\n        this.size++;\n    }\n\n    private void grow() {\n        String[] bigger = new String[this.songs.length * 2];\n        for (int i = 0; i < this.size; i++) {\n            bigger[i] = this.songs[i];\n        }\n        this.songs = bigger;\n    }\n\n    public int size() {\n        return this.size;\n    }\n\n    public int capacity() {\n        return this.songs.length;\n    }\n\n    @Override\n    public String toString() {\n        String text = \"[\";\n        for (int i = 0; i < this.size; i++) {\n            if (i > 0) {\n                text += \", \";\n            }\n            text += this.songs[i];\n        }\n        return text + \"]\";\n    }\n}\n```\n\n```output\nsize 1, array length 4\nsize 2, array length 4\nsize 3, array length 4\nsize 4, array length 4\nsize 5, array length 8\nsize 6, array length 8\n[Morning Rain, Night Train, Paper Boats, Slow River, Copper Sky, Lantern Light]\n```\n\n\n<figure class=\"uml\"><div class=\"uml-scroll\" tabindex=\"0\" role=\"group\" aria-label=\"Class diagram of Playlist\">\n<svg class=\"uml-svg\" viewBox=\"0 0 201 180\" width=\"201\" height=\"180\" style=\"width:12.57rem;min-width:11.52rem;aspect-ratio:201/180\" font-size=\"12\" aria-hidden=\"true\" focusable=\"false\"><rect class=\"u-bg\" fill=\"none\" x=\"6\" y=\"6\" width=\"189\" height=\"168\"/><rect class=\"u-head\" fill=\"none\" x=\"6\" y=\"6\" width=\"189\" height=\"24\"/><text x=\"100.5\" y=\"22\" text-anchor=\"middle\" class=\"u-name\">Playlist</text><text x=\"14\" y=\"46\">-songs: String[]</text><text x=\"14\" y=\"62\">-size: int</text><text x=\"14\" y=\"86\">+Playlist()</text><text x=\"14\" y=\"102\">+add(song: String): void</text><text x=\"14\" y=\"118\">+size(): int</text><text x=\"14\" y=\"134\">+capacity(): int</text><text x=\"14\" y=\"150\">+toString(): String</text><text x=\"14\" y=\"166\">-grow(): void</text><path class=\"u-sep\" fill=\"none\" d=\"M6 30h189M6 70h189\"/><rect class=\"u-frame\" fill=\"none\" x=\"6\" y=\"6\" width=\"189\" height=\"168\"/></svg>\n</div><details class=\"uml-text\"><summary>The diagram as text</summary><ul><li><strong>Playlist</strong>, a class<ul><li>private field <code>songs: String[]</code></li><li>private field <code>size: int</code></li><li>public constructor <code>Playlist()</code></li><li>public method <code>add(song: String): void</code></li><li>public method <code>size(): int</code></li><li>public method <code>capacity(): int</code></li><li>public method <code>toString(): String</code></li><li>private method <code>grow(): void</code></li></ul></li></ul></details></figure>\n\n\n- The instance variables are the array and the count from step 1, here called `size`. Both are private: code that uses a Playlist can't touch the array. It sees only the public methods, and it doesn't need to know that there's an array inside.\n- `add` first checks whether the array is full: when `size` equals the array's length, there's no free place, and `grow` makes room. Then the song goes to index `size`, and `size` goes up by one.\n- `grow` is private: it's a helper for the class's own methods, not something for other code to call. It copies the first `size` songs, which at that moment is every place in the array.\n- `capacity()` is here only to watch the array grow. Java's ArrayList has no such method: how long its array is stays its own business.\n- `toString` builds the text the way Java's lists print: the songs in square brackets, separated by commas. It goes up to `size`, so the empty places don't show.\n\n### How often does it grow?\n\nEach `grow` copies every song added so far, which sounds slow. But each time the array becomes twice as long, so the copying gets rarer as the list gets longer. Starting with room for 4, a list of 1000 songs grows 8 times: to 8, 16, 32, 64, 128, 256, 512 and 1024 places. All the copying adds up to 4 + 8 + 16 + 32 + 64 + 128 + 256 + 512 = 1020 values: about one copy for each song added. That's why `add` stays fast on average, however long the list gets.\n\nGrowing by a fixed amount would be much worse. With 4 more places each time, the same 1000 songs would mean 249 copies of the array, and 124,500 values copied in all.\n\nJava's own ArrayList grows the same way, only by a smaller step. In OpenJDK, whose class library this site uses, each new array is about one and a half times as long as the old one. The exact factor is an implementation detail: Java's documentation promises only that `add` stays fast on average.\n","task":"An event keeps its guest list in an array. In `GuestList.java`, write the class `GuestList` with:\n\n- a constructor with no parameters, which creates a `String` array with room for 2 names\n- `add(String name)`, which adds a name at the end; when the array is full, it first moves the names into an array twice as long\n- `size()`, which returns the number of names added\n- `toString()`, which returns the names in the order they were added, separated by a comma and a space, without brackets; a list without names gives the empty String\n\nKeep the names in the array, not in an ArrayList. The check runs:\n\n```java\nGuestList guests = new GuestList();\nguests.add(\"Amara\");\nguests.add(\"Bruno\");\nguests.add(\"Chen\");\nSystem.out.println(guests.size());\nSystem.out.println(guests);\n```\n\nand expects:\n\n```\n3\nAmara, Bruno, Chen\n```\n","kind":"code","seed":"// ==== GuestList.java ====\npublic class GuestList {\n\n}\n\n// ==== Main.java ====\npublic class Main {\n    public static void main(String[] args) {\n        GuestList guests = new GuestList();\n        guests.add(\"Amara\");\n        guests.add(\"Bruno\");\n        guests.add(\"Chen\");\n        System.out.println(guests.size());\n        System.out.println(guests);\n    }\n}\n","solution":"// ==== GuestList.java ====\npublic class GuestList {\n    private String[] names;\n    private int size;\n\n    public GuestList() {\n        this.names = new String[2];\n        this.size = 0;\n    }\n\n    public void add(String name) {\n        if (this.size == this.names.length) {\n            grow();\n        }\n        this.names[this.size] = name;\n        this.size++;\n    }\n\n    private void grow() {\n        String[] bigger = new String[this.names.length * 2];\n        for (int i = 0; i < this.size; i++) {\n            bigger[i] = this.names[i];\n        }\n        this.names = bigger;\n    }\n\n    public int size() {\n        return this.size;\n    }\n\n    @Override\n    public String toString() {\n        String text = \"\";\n        for (int i = 0; i < this.size; i++) {\n            if (i > 0) {\n                text += \", \";\n            }\n            text += this.names[i];\n        }\n        return text;\n    }\n}\n\n// ==== Main.java ====\npublic class Main {\n    public static void main(String[] args) {\n        GuestList guests = new GuestList();\n        guests.add(\"Amara\");\n        guests.add(\"Bruno\");\n        guests.add(\"Chen\");\n        System.out.println(guests.size());\n        System.out.println(guests);\n    }\n}\n","hints":["Two instance variables, as in Playlist: `private String[] names;` and `private int size;`. The constructor sets `this.names = new String[2];`.","In `add`, check `if (this.size == this.names.length)` first, and then grow: a new array of twice the length, a loop that copies the names, and `this.names = bigger;`. After that, `this.names[this.size] = name;` and `this.size++;`."],"tests":[{"name":"Test 1","stdin":"","call":"GuestList guests = new GuestList();\nguests.add(\"Amara\");\nguests.add(\"Bruno\");\nguests.add(\"Chen\");\nSystem.out.println(guests.size());\nSystem.out.println(guests);\n","expect":"3\nAmara, Bruno, Chen","hidden":false},{"name":"Test 2","stdin":"","call":"GuestList guests = new GuestList();\nSystem.out.println(guests.size() + \" [\" + guests + \"]\");\nguests.add(\"Dmitri\");\nSystem.out.println(guests.size() + \" [\" + guests + \"]\");\nguests.add(\"Eva\");\nSystem.out.println(guests.size() + \" [\" + guests + \"]\");\n","expect":"0 []\n1 [Dmitri]\n2 [Dmitri, Eva]","hidden":true},{"name":"Test 3","stdin":"","call":"GuestList guests = new GuestList();\nfor (int i = 1; i <= 50; i++) {\n    guests.add(\"Guest \" + i);\n}\nString text = guests.toString();\nSystem.out.println(guests.size());\nSystem.out.println(text.substring(0, 25));\nSystem.out.println(text.substring(text.length() - 20));\n","expect":"50\nGuest 1, Guest 2, Guest 3\n, Guest 49, Guest 50","hidden":true}],"require":[{"pattern":"\\bnew\\s+String\\s*\\[","message":"Keep the names in a `String` array, created with `new String[...]`."}],"forbid":[{"pattern":"\\b(ArrayList|LinkedList|List)\\b","message":"Keep the names in an array, without a list: the array that grows is the point."}],"more":[{"task":"This Playlist's constructor takes the length of its first array. What does this program print?\n","kind":"predict","seed":"public class Main {\n    public static void main(String[] args) {\n        Playlist party = new Playlist(3);\n        party.add(\"Copper Sky\");\n        party.add(\"Night Train\");\n        System.out.println(party.size() + \" \" + party.capacity());\n        party.add(\"Paper Boats\");\n        party.add(\"Slow River\");\n        System.out.println(party.size() + \" \" + party.capacity());\n        party.add(\"Morning Rain\");\n        party.add(\"Lantern Light\");\n        party.add(\"Copper Sky\");\n        System.out.println(party.size() + \" \" + party.capacity());\n    }\n}\n// ==== Playlist.java ====\npublic class Playlist {\n    private String[] songs;\n    private int size;\n\n    public Playlist(int room) {\n        this.songs = new String[room];\n        this.size = 0;\n    }\n\n    public void add(String song) {\n        if (this.size == this.songs.length) {\n            grow();\n        }\n        this.songs[this.size] = song;\n        this.size++;\n    }\n\n    private void grow() {\n        String[] bigger = new String[this.songs.length * 2];\n        for (int i = 0; i < this.size; i++) {\n            bigger[i] = this.songs[i];\n        }\n        this.songs = bigger;\n    }\n\n    public int size() {\n        return this.size;\n    }\n\n    public int capacity() {\n        return this.songs.length;\n    }\n}\n","solution":"public class Main {\n    public static void main(String[] args) {\n        Playlist party = new Playlist(3);\n        party.add(\"Copper Sky\");\n        party.add(\"Night Train\");\n        System.out.println(party.size() + \" \" + party.capacity());\n        party.add(\"Paper Boats\");\n        party.add(\"Slow River\");\n        System.out.println(party.size() + \" \" + party.capacity());\n        party.add(\"Morning Rain\");\n        party.add(\"Lantern Light\");\n        party.add(\"Copper Sky\");\n        System.out.println(party.size() + \" \" + party.capacity());\n    }\n}\n// ==== Playlist.java ====\npublic class Playlist {\n    private String[] songs;\n    private int size;\n\n    public Playlist(int room) {\n        this.songs = new String[room];\n        this.size = 0;\n    }\n\n    public void add(String song) {\n        if (this.size == this.songs.length) {\n            grow();\n        }\n        this.songs[this.size] = song;\n        this.size++;\n    }\n\n    private void grow() {\n        String[] bigger = new String[this.songs.length * 2];\n        for (int i = 0; i < this.size; i++) {\n            bigger[i] = this.songs[i];\n        }\n        this.songs = bigger;\n    }\n\n    public int size() {\n        return this.size;\n    }\n\n    public int capacity() {\n        return this.songs.length;\n    }\n}\n","hints":["The array grows only when a song arrives and every place is taken. The first two songs fit in the 3 places.","The fourth song finds 3 of 3 places used, so the array becomes 6 long. The seventh song finds 6 of 6 used. A song added twice still takes a place of its own."],"tests":[{"name":"Output","stdin":"","expect":"2 3\n4 6\n7 12","hidden":false}],"require":[],"forbid":[],"lines":["2 3","4 6","7 12"]},{"task":"A science center's turnstile counts the people who come in during each hour. `TurnstileLog.java` keeps the counts in an `int` array that should grow, but the program crashes with an ArrayIndexOutOfBoundsException as soon as the third hour's count is added. There are two mistakes in `TurnstileLog.java`, one in `add` and one in `grow`. Fix them. The check runs:\n\n```java\nTurnstileLog log = new TurnstileLog();\nint[] hours = {14, 3, 22, 9, 17};\nfor (int people : hours) {\n    log.add(people);\n}\nSystem.out.println(log.size() + \" hours, \" + log.total() + \" people\");\n```\n\nand expects:\n\n```\n5 hours, 65 people\n```\n","kind":"code","seed":"// ==== TurnstileLog.java ====\npublic class TurnstileLog {\n    private int[] values;\n    private int size;\n\n    public TurnstileLog() {\n        this.values = new int[2];\n        this.size = 0;\n    }\n\n    public void add(int people) {\n        if (this.size > this.values.length) {\n            grow();\n        }\n        this.values[this.size] = people;\n        this.size++;\n    }\n\n    private void grow() {\n        int[] bigger = new int[this.values.length * 2];\n        for (int i = 0; i < this.size; i++) {\n            bigger[i] = this.values[i];\n        }\n    }\n\n    public int size() {\n        return this.size;\n    }\n\n    public int total() {\n        int sum = 0;\n        for (int i = 0; i < this.size; i++) {\n            sum += this.values[i];\n        }\n        return sum;\n    }\n}\n\n// ==== Main.java ====\npublic class Main {\n    public static void main(String[] args) {\n        TurnstileLog log = new TurnstileLog();\n        int[] hours = {14, 3, 22, 9, 17};\n        for (int people : hours) {\n            log.add(people);\n        }\n        System.out.println(log.size() + \" hours, \" + log.total() + \" people\");\n    }\n}\n","solution":"// ==== TurnstileLog.java ====\npublic class TurnstileLog {\n    private int[] values;\n    private int size;\n\n    public TurnstileLog() {\n        this.values = new int[2];\n        this.size = 0;\n    }\n\n    public void add(int people) {\n        if (this.size == this.values.length) {\n            grow();\n        }\n        this.values[this.size] = people;\n        this.size++;\n    }\n\n    private void grow() {\n        int[] bigger = new int[this.values.length * 2];\n        for (int i = 0; i < this.size; i++) {\n            bigger[i] = this.values[i];\n        }\n        this.values = bigger;\n    }\n\n    public int size() {\n        return this.size;\n    }\n\n    public int total() {\n        int sum = 0;\n        for (int i = 0; i < this.size; i++) {\n            sum += this.values[i];\n        }\n        return sum;\n    }\n}\n\n// ==== Main.java ====\npublic class Main {\n    public static void main(String[] args) {\n        TurnstileLog log = new TurnstileLog();\n        int[] hours = {14, 3, 22, 9, 17};\n        for (int people : hours) {\n            log.add(people);\n        }\n        System.out.println(log.size() + \" hours, \" + log.total() + \" people\");\n    }\n}\n","hints":["With 2 values in an array of length 2, `this.size > this.values.length` is `2 > 2`, which is false, so `grow` isn't called, and the value goes to index 2. The array is full exactly when the two are equal.","`grow` fills a new array, but `this.values` still refers to the old one when the method ends. Add `this.values = bigger;` after the loop."],"tests":[{"name":"Test 1","stdin":"","call":"TurnstileLog log = new TurnstileLog();\nint[] hours = {14, 3, 22, 9, 17};\nfor (int people : hours) {\n    log.add(people);\n}\nSystem.out.println(log.size() + \" hours, \" + log.total() + \" people\");\n","expect":"5 hours, 65 people","hidden":false},{"name":"Test 2","stdin":"","call":"TurnstileLog log = new TurnstileLog();\nSystem.out.println(log.size() + \" \" + log.total());\nlog.add(9);\nlog.add(1);\nSystem.out.println(log.size() + \" \" + log.total());\n","expect":"0 0\n2 10","hidden":true},{"name":"Test 3","stdin":"","call":"TurnstileLog log = new TurnstileLog();\nfor (int hour = 1; hour <= 40; hour++) {\n    log.add(hour % 6);\n}\nSystem.out.println(log.size() + \" \" + log.total());\n","expect":"40 100","hidden":true}],"require":[],"forbid":[]}]}