{"id":"linked-structures-list-class","slug":"a-linked-list","title":"A linked list class","text":"Step 1 changed the links by hand in `main`, where one wrong assignment cuts off the rest of the chain. A list class keeps its nodes to itself: the code that uses it calls methods such as `addFirst`, and never sees a node. Here's a reading list of book titles, with the newest wish at the front:\n\n```java\npublic class Main {\n    public static void main(String[] args) {\n        ReadingList list = new ReadingList();\n        list.addFirst(\"Emma\");\n        list.addFirst(\"Dune\");\n        list.addFirst(\"Beloved\");\n        System.out.println(list.size() + \" books\");\n        list.printAll();\n    }\n}\n// ==== ReadingList.java ====\npublic class ReadingList {\n    private TitleNode head;\n    private int size;\n\n    public ReadingList() {\n        this.head = null;\n        this.size = 0;\n    }\n\n    public void addFirst(String title) {\n        this.head = new TitleNode(title, this.head);\n        this.size++;\n    }\n\n    public int size() {\n        return this.size;\n    }\n\n    public void printAll() {\n        TitleNode current = this.head;\n        while (current != null) {\n            System.out.println(current.title);\n            current = current.next;\n        }\n    }\n}\n// ==== TitleNode.java ====\npublic class TitleNode {\n    String title;\n    TitleNode next;\n\n    public TitleNode(String title, TitleNode next) {\n        this.title = title;\n        this.next = next;\n    }\n}\n```\n\n```output\n3 books\nBeloved\nDune\nEmma\n```\n\n\n<figure class=\"uml\"><div class=\"uml-scroll\" tabindex=\"0\" role=\"group\" aria-label=\"Class diagram of ReadingList and TitleNode\">\n<svg class=\"uml-svg\" viewBox=\"0 0 331 280\" width=\"331\" height=\"280\" style=\"width:20.69rem;min-width:18.97rem;aspect-ratio:331/280\" font-size=\"12\" aria-hidden=\"true\" focusable=\"false\"><path class=\"u-edge\" fill=\"none\" d=\"M165.5 142L165.5 186\"/><path class=\"u-edge\" fill=\"none\" d=\"M160 176L165.5 186L171 176\"/><rect class=\"u-bg\" fill=\"none\" x=\"49.5\" y=\"6\" width=\"232\" height=\"136\"/><rect class=\"u-head\" fill=\"none\" x=\"49.5\" y=\"6\" width=\"232\" height=\"24\"/><text x=\"165.5\" y=\"22\" text-anchor=\"middle\" class=\"u-name\">ReadingList</text><text x=\"57.5\" y=\"46\">-head: TitleNode</text><text x=\"57.5\" y=\"62\">-size: int</text><text x=\"57.5\" y=\"86\">+ReadingList()</text><text x=\"57.5\" y=\"102\">+addFirst(title: String): void</text><text x=\"57.5\" y=\"118\">+size(): int</text><text x=\"57.5\" y=\"134\">+printAll(): void</text><path class=\"u-sep\" fill=\"none\" d=\"M49.5 30h232M49.5 70h232\"/><rect class=\"u-frame\" fill=\"none\" x=\"49.5\" y=\"6\" width=\"232\" height=\"136\"/><rect class=\"u-bg\" fill=\"none\" x=\"6\" y=\"186\" width=\"319\" height=\"88\"/><rect class=\"u-head\" fill=\"none\" x=\"6\" y=\"186\" width=\"319\" height=\"24\"/><text x=\"165.5\" y=\"202\" text-anchor=\"middle\" class=\"u-name\">TitleNode</text><text x=\"14\" y=\"226\">~title: String</text><text x=\"14\" y=\"242\">~next: TitleNode</text><text x=\"14\" y=\"266\">+TitleNode(title: String, next: TitleNode)</text><path class=\"u-sep\" fill=\"none\" d=\"M6 210h319M6 250h319\"/><rect class=\"u-frame\" fill=\"none\" x=\"6\" y=\"186\" width=\"319\" height=\"88\"/><text x=\"173.5\" y=\"181\" class=\"u-mult\">0..1</text><text x=\"159.5\" y=\"168\" text-anchor=\"end\" class=\"u-role\">head</text></svg>\n</div><details class=\"uml-text\"><summary>The diagram as text</summary><ul><li><strong>ReadingList</strong>, a class<ul><li>private field <code>head: TitleNode</code></li><li>private field <code>size: int</code></li><li>public constructor <code>ReadingList()</code></li><li>public method <code>addFirst(title: String): void</code></li><li>public method <code>size(): int</code></li><li>public method <code>printAll(): void</code></li></ul></li><li><strong>TitleNode</strong>, a class<ul><li>package-private field <code>title: String</code></li><li>package-private field <code>next: TitleNode</code></li><li>public constructor <code>TitleNode(title: String, next: TitleNode)</code></li></ul></li></ul><p>Connections:</p><ul><li>Each ReadingList has at most one TitleNode object (head).</li></ul></details></figure>\n\n\nEach TitleNode's `next` refers to another TitleNode (a diagram can't draw a class connected to itself, so it's shown as the field).\n\n- `head` refers to the first node, or is `null` while the list is empty.\n- `addFirst` makes a node whose `next` is the old first node, and then moves `head` to the new node. On an empty list, the old head is `null`, so the new node's `next` is `null`: the same line works for both.\n- The list counts its values in `size`. Counting the nodes would mean walking the whole list every time.\n\nAdding at the front takes the same few steps however long the list is. An ArrayList's `add(0, value)` has to move every value in its array one place along first (module 45).\n\n### Adding at the end\n\nTo add at the end, the list needs its last node, so that it can set that node's `next`. Walking from `head` to the last node takes one step per node:\n\n```java\nTitleNode current = this.head;\nwhile (current.next != null) {\n    current = current.next;\n}\ncurrent.next = new TitleNode(title, null);\n```\n\nThis loop also crashes on an empty list, as in step 1. A list that adds at the end often keeps a second reference, `tail`, to its last node. Then adding at the end takes a few steps too: link the new node after `tail`, and move `tail` to it. Every method that changes the list must keep `tail` right. When the list is empty, `head` and `tail` are both `null`; after the first value, both refer to the same node.\n\n### Reading and removing\n\n- **get(index)** has no array to jump into: it starts at `head` and follows `next` index times. An ArrayList's `get` goes straight to the place in its array, so it takes the same steps for any index; a linked list's `get(1000)` follows 1,000 links.\n- **removeFirst()** moves `head` to the second node, `this.head = this.head.next;`. Nothing refers to the old first node any more, so Java's garbage collector can free its memory. When the list becomes empty, `tail` must become `null` too, or it would still refer to the removed node.\n- Bad input gets an exception, as in module 45: `throw new IndexOutOfBoundsException(...)` for an index outside the list.\n","task":"`ReadingList.java` is the class above, without `printAll`. Give it a method `toString()` that returns the titles from front to back, in square brackets and separated by a comma and a space, like an ArrayList prints: `[Beloved, Dune, Emma]`. An empty list gives `[]`. The check runs:\n\n```java\nReadingList list = new ReadingList();\nSystem.out.println(list);\nlist.addFirst(\"Emma\");\nlist.addFirst(\"Dune\");\nlist.addFirst(\"Beloved\");\nSystem.out.println(list);\nSystem.out.println(list.size());\n```\n\nand expects:\n\n```\n[]\n[Beloved, Dune, Emma]\n3\n```\n","kind":"code","seed":"// ==== ReadingList.java ====\npublic class ReadingList {\n    private TitleNode head;\n    private int size;\n\n    public ReadingList() {\n        this.head = null;\n        this.size = 0;\n    }\n\n    public void addFirst(String title) {\n        this.head = new TitleNode(title, this.head);\n        this.size++;\n    }\n\n    public int size() {\n        return this.size;\n    }\n}\n\n// ==== TitleNode.java ====\npublic class TitleNode {\n    String title;\n    TitleNode next;\n\n    public TitleNode(String title, TitleNode next) {\n        this.title = title;\n        this.next = next;\n    }\n}\n\n// ==== Main.java ====\npublic class Main {\n    public static void main(String[] args) {\n        ReadingList list = new ReadingList();\n        System.out.println(list);\n        list.addFirst(\"Emma\");\n        list.addFirst(\"Dune\");\n        list.addFirst(\"Beloved\");\n        System.out.println(list);\n        System.out.println(list.size());\n    }\n}\n","solution":"// ==== ReadingList.java ====\npublic class ReadingList {\n    private TitleNode head;\n    private int size;\n\n    public ReadingList() {\n        this.head = null;\n        this.size = 0;\n    }\n\n    public void addFirst(String title) {\n        this.head = new TitleNode(title, this.head);\n        this.size++;\n    }\n\n    public int size() {\n        return this.size;\n    }\n\n    @Override\n    public String toString() {\n        String result = \"[\";\n        TitleNode current = this.head;\n        while (current != null) {\n            result += current.title;\n            if (current.next != null) {\n                result += \", \";\n            }\n            current = current.next;\n        }\n        return result + \"]\";\n    }\n}\n\n// ==== TitleNode.java ====\npublic class TitleNode {\n    String title;\n    TitleNode next;\n\n    public TitleNode(String title, TitleNode next) {\n        this.title = title;\n        this.next = next;\n    }\n}\n\n// ==== Main.java ====\npublic class Main {\n    public static void main(String[] args) {\n        ReadingList list = new ReadingList();\n        System.out.println(list);\n        list.addFirst(\"Emma\");\n        list.addFirst(\"Dune\");\n        list.addFirst(\"Beloved\");\n        System.out.println(list);\n        System.out.println(list.size());\n    }\n}\n","hints":["The header is `public String toString()`, with `@Override` above it. Walk from `this.head` with a `current` variable, as `printAll` did, and build a String.","Put `, ` between titles, not after the last one: add it only when `current.next != null`. Start with `[` and end with `]`."],"tests":[{"name":"Test 1","stdin":"","call":"ReadingList list = new ReadingList();\nSystem.out.println(list);\nlist.addFirst(\"Emma\");\nlist.addFirst(\"Dune\");\nlist.addFirst(\"Beloved\");\nSystem.out.println(list);\nSystem.out.println(list.size());\n","expect":"[]\n[Beloved, Dune, Emma]\n3","hidden":false},{"name":"Test 2","stdin":"","call":"ReadingList list = new ReadingList();\nlist.addFirst(\"Ulysses\");\nSystem.out.println(list);\nlist.addFirst(\"Middlemarch\");\nSystem.out.println(list);\nlist.addFirst(\"Kindred\");\nlist.addFirst(\"Persuasion\");\nSystem.out.println(list + \" \" + list.size());\n","expect":"[Ulysses]\n[Middlemarch, Ulysses]\n[Persuasion, Kindred, Middlemarch, Ulysses] 4","hidden":true}],"require":[],"forbid":[],"more":[{"task":"Give `ReadingList` a reference to its last node, so that it can add at the end in a few steps:\n\n- add an instance variable `private TitleNode tail;`;\n- make `addFirst` keep it right: after adding to an empty list, `tail` refers to the new node too;\n- write `addLast(String title)`, which adds the title at the end, using `tail` and no loop.\n\nThe check runs:\n\n```java\nReadingList list = new ReadingList();\nlist.addLast(\"Emma\");\nlist.addLast(\"Dune\");\nlist.addFirst(\"Beloved\");\nlist.addLast(\"Ulysses\");\nSystem.out.println(list);\nSystem.out.println(list.size());\n```\n\nand expects:\n\n```\n[Beloved, Emma, Dune, Ulysses]\n4\n```\n","kind":"code","seed":"// ==== ReadingList.java ====\npublic class ReadingList {\n    private TitleNode head;\n    private int size;\n\n    public ReadingList() {\n        this.head = null;\n        this.size = 0;\n    }\n\n    public void addFirst(String title) {\n        this.head = new TitleNode(title, this.head);\n        this.size++;\n    }\n\n    public void addLast(String title) {\n\n    }\n\n    public int size() {\n        return this.size;\n    }\n\n    @Override\n    public String toString() {\n        String result = \"[\";\n        TitleNode current = this.head;\n        while (current != null) {\n            result += current.title;\n            if (current.next != null) {\n                result += \", \";\n            }\n            current = current.next;\n        }\n        return result + \"]\";\n    }\n}\n\n// ==== TitleNode.java ====\npublic class TitleNode {\n    String title;\n    TitleNode next;\n\n    public TitleNode(String title, TitleNode next) {\n        this.title = title;\n        this.next = next;\n    }\n}\n\n// ==== Main.java ====\npublic class Main {\n    public static void main(String[] args) {\n        ReadingList list = new ReadingList();\n        list.addLast(\"Emma\");\n        list.addLast(\"Dune\");\n        list.addFirst(\"Beloved\");\n        list.addLast(\"Ulysses\");\n        System.out.println(list);\n        System.out.println(list.size());\n    }\n}\n","solution":"// ==== ReadingList.java ====\npublic class ReadingList {\n    private TitleNode head;\n    private TitleNode tail;\n    private int size;\n\n    public ReadingList() {\n        this.head = null;\n        this.tail = null;\n        this.size = 0;\n    }\n\n    public void addFirst(String title) {\n        this.head = new TitleNode(title, this.head);\n        if (this.tail == null) {\n            this.tail = this.head;\n        }\n        this.size++;\n    }\n\n    public void addLast(String title) {\n        TitleNode node = new TitleNode(title, null);\n        if (this.tail == null) {\n            this.head = node;\n        } else {\n            this.tail.next = node;\n        }\n        this.tail = node;\n        this.size++;\n    }\n\n    public int size() {\n        return this.size;\n    }\n\n    @Override\n    public String toString() {\n        String result = \"[\";\n        TitleNode current = this.head;\n        while (current != null) {\n            result += current.title;\n            if (current.next != null) {\n                result += \", \";\n            }\n            current = current.next;\n        }\n        return result + \"]\";\n    }\n}\n\n// ==== TitleNode.java ====\npublic class TitleNode {\n    String title;\n    TitleNode next;\n\n    public TitleNode(String title, TitleNode next) {\n        this.title = title;\n        this.next = next;\n    }\n}\n\n// ==== Main.java ====\npublic class Main {\n    public static void main(String[] args) {\n        ReadingList list = new ReadingList();\n        list.addLast(\"Emma\");\n        list.addLast(\"Dune\");\n        list.addFirst(\"Beloved\");\n        list.addLast(\"Ulysses\");\n        System.out.println(list);\n        System.out.println(list.size());\n    }\n}\n","hints":["In `addLast`, make the new node with `next` as `null`. If the list is empty (`tail` is `null`), it's the first node too: `this.head = node;`. Otherwise link it after the last node: `this.tail.next = node;`. Either way, `this.tail = node;` and add 1 to the size.","In `addFirst`, the new node is also the last one when the list was empty: after setting `head`, `if (this.tail == null) { this.tail = this.head; }`."],"tests":[{"name":"Test 1","stdin":"","call":"ReadingList list = new ReadingList();\nlist.addLast(\"Emma\");\nlist.addLast(\"Dune\");\nlist.addFirst(\"Beloved\");\nlist.addLast(\"Ulysses\");\nSystem.out.println(list);\nSystem.out.println(list.size());\n","expect":"[Beloved, Emma, Dune, Ulysses]\n4","hidden":false},{"name":"Test 2","stdin":"","call":"ReadingList list = new ReadingList();\nlist.addFirst(\"Kindred\");\nlist.addLast(\"Persuasion\");\nlist.addLast(\"Middlemarch\");\nSystem.out.println(list + \" \" + list.size());\nReadingList other = new ReadingList();\nother.addFirst(\"Ulysses\");\nother.addFirst(\"Emma\");\nother.addLast(\"Dune\");\nSystem.out.println(other + \" \" + other.size());\n","expect":"[Kindred, Persuasion, Middlemarch] 3\n[Emma, Ulysses, Dune] 3","hidden":true}],"require":[{"pattern":"TitleNode\\s+tail\\b","message":"Keep the last node in an instance variable: `private TitleNode tail;`."}],"forbid":[]},{"task":"`ReadingList` now has `head`, `tail`, `addFirst`, `addLast` and `toString`. Write two more methods:\n\n- `get(int index)` returns the title at the index, counting from 0 at the front. For an index below 0 or at least the size, it throws an `IndexOutOfBoundsException` with the message `Index 5 out of bounds for size 3` (with the index and the size it got).\n- `removeFirst()` removes the first title and returns it. On an empty list it throws an `IllegalStateException` with the message `The reading list is empty`. When the last title goes, `tail` must become `null` too, so that `addLast` still works afterwards.\n\nThe check runs:\n\n```java\nReadingList list = new ReadingList();\nlist.addLast(\"Emma\");\nlist.addLast(\"Dune\");\nlist.addLast(\"Ulysses\");\nSystem.out.println(list.get(0) + \", \" + list.get(2));\ntry {\n    list.get(5);\n} catch (IndexOutOfBoundsException e) {\n    System.out.println(e.getMessage());\n}\nSystem.out.println(\"Read \" + list.removeFirst());\nSystem.out.println(list + \" \" + list.size());\n```\n\nand expects:\n\n```\nEmma, Ulysses\nIndex 5 out of bounds for size 3\nRead Emma\n[Dune, Ulysses] 2\n```\n","kind":"code","seed":"// ==== ReadingList.java ====\npublic class ReadingList {\n    private TitleNode head;\n    private TitleNode tail;\n    private int size;\n\n    public ReadingList() {\n        this.head = null;\n        this.tail = null;\n        this.size = 0;\n    }\n\n    public void addFirst(String title) {\n        this.head = new TitleNode(title, this.head);\n        if (this.tail == null) {\n            this.tail = this.head;\n        }\n        this.size++;\n    }\n\n    public void addLast(String title) {\n        TitleNode node = new TitleNode(title, null);\n        if (this.tail == null) {\n            this.head = node;\n        } else {\n            this.tail.next = node;\n        }\n        this.tail = node;\n        this.size++;\n    }\n\n    public String get(int index) {\n        return null;\n    }\n\n    public String removeFirst() {\n        return null;\n    }\n\n    public int size() {\n        return this.size;\n    }\n\n    @Override\n    public String toString() {\n        String result = \"[\";\n        TitleNode current = this.head;\n        while (current != null) {\n            result += current.title;\n            if (current.next != null) {\n                result += \", \";\n            }\n            current = current.next;\n        }\n        return result + \"]\";\n    }\n}\n\n// ==== TitleNode.java ====\npublic class TitleNode {\n    String title;\n    TitleNode next;\n\n    public TitleNode(String title, TitleNode next) {\n        this.title = title;\n        this.next = next;\n    }\n}\n\n// ==== Main.java ====\npublic class Main {\n    public static void main(String[] args) {\n        ReadingList list = new ReadingList();\n        list.addLast(\"Emma\");\n        list.addLast(\"Dune\");\n        list.addLast(\"Ulysses\");\n        System.out.println(list.get(0) + \", \" + list.get(2));\n        try {\n            list.get(5);\n        } catch (IndexOutOfBoundsException e) {\n            System.out.println(e.getMessage());\n        }\n        System.out.println(\"Read \" + list.removeFirst());\n        System.out.println(list + \" \" + list.size());\n    }\n}\n","solution":"// ==== ReadingList.java ====\npublic class ReadingList {\n    private TitleNode head;\n    private TitleNode tail;\n    private int size;\n\n    public ReadingList() {\n        this.head = null;\n        this.tail = null;\n        this.size = 0;\n    }\n\n    public void addFirst(String title) {\n        this.head = new TitleNode(title, this.head);\n        if (this.tail == null) {\n            this.tail = this.head;\n        }\n        this.size++;\n    }\n\n    public void addLast(String title) {\n        TitleNode node = new TitleNode(title, null);\n        if (this.tail == null) {\n            this.head = node;\n        } else {\n            this.tail.next = node;\n        }\n        this.tail = node;\n        this.size++;\n    }\n\n    public String get(int index) {\n        if (index < 0 || index >= this.size) {\n            throw new IndexOutOfBoundsException(\"Index \" + index + \" out of bounds for size \" + this.size);\n        }\n        TitleNode current = this.head;\n        for (int i = 0; i < index; i++) {\n            current = current.next;\n        }\n        return current.title;\n    }\n\n    public String removeFirst() {\n        if (this.head == null) {\n            throw new IllegalStateException(\"The reading list is empty\");\n        }\n        String title = this.head.title;\n        this.head = this.head.next;\n        if (this.head == null) {\n            this.tail = null;\n        }\n        this.size--;\n        return title;\n    }\n\n    public int size() {\n        return this.size;\n    }\n\n    @Override\n    public String toString() {\n        String result = \"[\";\n        TitleNode current = this.head;\n        while (current != null) {\n            result += current.title;\n            if (current.next != null) {\n                result += \", \";\n            }\n            current = current.next;\n        }\n        return result + \"]\";\n    }\n}\n\n// ==== TitleNode.java ====\npublic class TitleNode {\n    String title;\n    TitleNode next;\n\n    public TitleNode(String title, TitleNode next) {\n        this.title = title;\n        this.next = next;\n    }\n}\n\n// ==== Main.java ====\npublic class Main {\n    public static void main(String[] args) {\n        ReadingList list = new ReadingList();\n        list.addLast(\"Emma\");\n        list.addLast(\"Dune\");\n        list.addLast(\"Ulysses\");\n        System.out.println(list.get(0) + \", \" + list.get(2));\n        try {\n            list.get(5);\n        } catch (IndexOutOfBoundsException e) {\n            System.out.println(e.getMessage());\n        }\n        System.out.println(\"Read \" + list.removeFirst());\n        System.out.println(list + \" \" + list.size());\n    }\n}\n","hints":["`get` first checks the index: `if (index < 0 || index >= this.size) { throw new IndexOutOfBoundsException(\"Index \" + index + \" out of bounds for size \" + this.size); }`. Then it starts at `head` and moves `current = current.next;` index times.","`removeFirst` keeps the first title in a variable, moves `head` on with `this.head = this.head.next;`, and lowers the size. If `head` is now `null`, the list is empty: set `tail` to `null` too."],"tests":[{"name":"Test 1","stdin":"","call":"ReadingList list = new ReadingList();\nlist.addLast(\"Emma\");\nlist.addLast(\"Dune\");\nlist.addLast(\"Ulysses\");\nSystem.out.println(list.get(0) + \", \" + list.get(2));\ntry {\n    list.get(5);\n} catch (IndexOutOfBoundsException e) {\n    System.out.println(e.getMessage());\n}\nSystem.out.println(\"Read \" + list.removeFirst());\nSystem.out.println(list + \" \" + list.size());\n","expect":"Emma, Ulysses\nIndex 5 out of bounds for size 3\nRead Emma\n[Dune, Ulysses] 2","hidden":false},{"name":"Test 2","stdin":"","call":"ReadingList list = new ReadingList();\nlist.addLast(\"Kindred\");\nlist.addFirst(\"Persuasion\");\nSystem.out.println(list.get(1));\ntry {\n    list.get(-1);\n} catch (IndexOutOfBoundsException e) {\n    System.out.println(e.getMessage());\n}\ntry {\n    list.get(2);\n} catch (IndexOutOfBoundsException e) {\n    System.out.println(e.getMessage());\n}\nSystem.out.println(list.removeFirst() + \" \" + list.removeFirst());\nSystem.out.println(list + \" \" + list.size());\ntry {\n    list.removeFirst();\n} catch (IllegalStateException e) {\n    System.out.println(e.getMessage());\n}\nlist.addLast(\"Middlemarch\");\nlist.addLast(\"Emma\");\nSystem.out.println(list + \" \" + list.size() + \" \" + list.get(1));\n","expect":"Kindred\nIndex -1 out of bounds for size 2\nIndex 2 out of bounds for size 2\nPersuasion Kindred\n[] 0\nThe reading list is empty\n[Middlemarch, Emma] 2 Emma","hidden":true}],"require":[{"pattern":"throw\\s+new\\s+IndexOutOfBoundsException","message":"Refuse a bad index with `throw new IndexOutOfBoundsException(...)`."},{"pattern":"throw\\s+new\\s+IllegalStateException","message":"Refuse to remove from an empty list with `throw new IllegalStateException(\"The reading list is empty\")`."}],"forbid":[]}]}