Java Arena
Step 2 of 6

A linked list class

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:

Main.java
public class Main {
    public static void main(String[] args) {
        ReadingList list = new ReadingList();
        list.addFirst("Emma");
        list.addFirst("Dune");
        list.addFirst("Beloved");
        System.out.println(list.size() + " books");
        list.printAll();
    }
}
ReadingList.java
public class ReadingList {
    private TitleNode head;
    private int size;

    public ReadingList() {
        this.head = null;
        this.size = 0;
    }

    public void addFirst(String title) {
        this.head = new TitleNode(title, this.head);
        this.size++;
    }

    public int size() {
        return this.size;
    }

    public void printAll() {
        TitleNode current = this.head;
        while (current != null) {
            System.out.println(current.title);
            current = current.next;
        }
    }
}
TitleNode.java
public class TitleNode {
    String title;
    TitleNode next;

    public TitleNode(String title, TitleNode next) {
        this.title = title;
        this.next = next;
    }
}
Output
3 books
Beloved
Dune
Emma
The diagram as text
  • ReadingList, a class
    • private field head: TitleNode
    • private field size: int
    • public constructor ReadingList()
    • public method addFirst(title: String): void
    • public method size(): int
    • public method printAll(): void
  • TitleNode, a class
    • package-private field title: String
    • package-private field next: TitleNode
    • public constructor TitleNode(title: String, next: TitleNode)

Connections:

  • Each ReadingList has at most one TitleNode object (head).

Each TitleNode's next refers to another TitleNode (a diagram can't draw a class connected to itself, so it's shown as the field).

  • head refers to the first node, or is null while the list is empty.
  • 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.
  • The list counts its values in size. Counting the nodes would mean walking the whole list every time.

Adding 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).

Adding at the end

To 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:

TitleNode current = this.head;
while (current.next != null) {
    current = current.next;
}
current.next = new TitleNode(title, null);

This 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.

Reading and removing

  • 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.
  • 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.
  • Bad input gets an exception, as in module 45: throw new IndexOutOfBoundsException(...) for an index outside the list.

Your task

Challenge 1 of 3

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:

ReadingList list = new ReadingList();
System.out.println(list);
list.addFirst("Emma");
list.addFirst("Dune");
list.addFirst("Beloved");
System.out.println(list);
System.out.println(list.size());

and expects:

[]
[Beloved, Dune, Emma]
3

Challenge 2

Give ReadingList a reference to its last node, so that it can add at the end in a few steps:

  • add an instance variable private TitleNode tail;;
  • make addFirst keep it right: after adding to an empty list, tail refers to the new node too;
  • write addLast(String title), which adds the title at the end, using tail and no loop.

The check runs:

ReadingList list = new ReadingList();
list.addLast("Emma");
list.addLast("Dune");
list.addFirst("Beloved");
list.addLast("Ulysses");
System.out.println(list);
System.out.println(list.size());

and expects:

[Beloved, Emma, Dune, Ulysses]
4

Challenge 3

ReadingList now has head, tail, addFirst, addLast and toString. Write two more methods:

  • 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).
  • 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.

The check runs:

ReadingList list = new ReadingList();
list.addLast("Emma");
list.addLast("Dune");
list.addLast("Ulysses");
System.out.println(list.get(0) + ", " + list.get(2));
try {
    list.get(5);
} catch (IndexOutOfBoundsException e) {
    System.out.println(e.getMessage());
}
System.out.println("Read " + list.removeFirst());
System.out.println(list + " " + list.size());

and expects:

Emma, Ulysses
Index 5 out of bounds for size 3
Read Emma
[Dune, Ulysses] 2