Lecture 08: More linked lists; Generics

Welcome

Announcements

In-class exercise

Suppose x is a node in the middle a large linked list. What will be the effect of x.setNext(x.getNext().getNext())?

Suppose x is a node at the end of large linked list. What will be the effect of x.setNext(x.getNext().getNext())?

Building StringLinkedList

We’ll need to keep a reference to the underlying list of nodes as an instance variable, and it turns out that’s the only instance variable we need. By convention, this is called head, and it’s initialized to null (an empty list) when we start:

private StringNode head;

public StringLinkedList() {
	head = null;
}

Let’s add the “simple” methods from last lecture, starting with size. What do we need to do? There’s no underlying array, so we can’t just examine the length attribute and return it. Instead, we need to traverse the list. In other words, (on board) we will start at the head element (if it exists), and follow the next references until we reach the end, signaled by a null value. Don’t forget that head is null, so your code should account for this:

public int size() {
	int size = 0;
	StringNode current = head;
	while (current != null) {
			size++;
			current = current.getNext();
	}
	return size;
}

Unlike arrays, where we can just jump to the node we want, a linked-list (almost) always requires that we traverse its elements until we get to the one we want.

Note that we don’t have to traverse the whole list for size; we could maintain a private size instance variable and just return it instead if we wanted to (and then, just like in StringArrayList, we need to remember to updated whenever adding or removing.) Let’s do so instead of the above:

	int size;
	StringNode head;
	
	public StringLinkedList() {
		size = 0;
		head = null;
	}
	
	@Override
	public int size() {
		return size;
	}

Now let’s do get. The exceptions are the same as before, but now we must traverse the list to find the ith element:

public String get(int i) throws IndexOutOfBoundsException {
	if (i < 0 || i >= size) {
		throw new IndexOutOfBoundsException();
	}
	int j = 0;
	StringNode current = head;
	while (current != null) {
		if (i == j) {
			return current.getContents();
		}
		current = current.getNext();
		j++;
	}
}

In class exercise

Suppose we remove i >= size from this check in get. What happens?

More on StringLinkedList

Now let’s look at add. If we want to add at the end of the list we can again traverse it to get to the end, and adjust the last element’s next reference appropriately. Note that we have to stop our traversal just before we get to the end, not after we go past it, which also means we have to special-case the head (on board first):

public void add(String s) {
	size++;
	Node node = new Node(s);
	
	// case 1: empty list
	if (head == null) {
		head = node;
		return;
	}
	
	// case 2: add to end of list
	Node current = head;
	while (current.getNext() != null) {
		current = current.getNext();
	}		
}

Similarly, if we want to add somewhere in the middle of the list, we have to stop just before we get there, and do surgery on both the previous and current (added) item to make the links in the list line up, again with a special case for the first spot:

public void add(int i, String s) throws IndexOutOfBoundsException {
	if (i < 0 || i > size) {
		throw new IndexOutOfBoundsException();
	}
	size++;
	
	Node node = new Node(s);  // step 1
	
	// case 1: at front of list
	if (i == 0) {
		node.setNext(head); // step 2
		head = node; // step 3
		return;
	}
	
	// case 2: somewhere in the list
	
	Node nodeBefore = head; // step 2
	for (int j = 0; j < i; j++) {
		nodeBefore = nodeBefore.getNext();;
	}
	
	node.setNext(nodeBefore.getNext()); // step 3
	
	nodeBefore.setNext(node);
}

Finishing up StringLinkedList

Finally the remove method, which again has to do some surgery on the previous node (if it exists). Let’s do some examples on the board first (end of list; front of list; middle of list)

Then here’s the code for remove():

public String remove(int i) throws IndexOutOfBoundsException {
	if (i < 0 || i >= size) {
		throw new IndexOutOfBoundsException();
	}
	
	size--;
	
	String result;
	
	// case 1: head of list
	if (i == 0) {
		result = head.getContents();
		head = head.getNext();
		return result;
	}
	
	// case 2: somewhere else
	Node nodeBefore = head; 
	for (int j = 0; j < i; j++) { // step 1
		nodeBefore = nodeBefore.getNext();
	}
	
	Node nodeToDelete = nodeBefore.getNext();
	
	result = nodeToDelete.getContents(); // step 2
	
	nodeBefore.setNext(nodeToDelete.getNext()); // step 3
	
	return result;
	
}

And we’ll run it in our toy program, switching StringArrayList to StringLinkedList. Notice the behavior doesn’t change. (StringListInterface sli = new StringLinkedList(); is the only change.)

OH NOES IT’S BROOOOKEN!

We’ll fix it in next lecture :)