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 i
th 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 :)