## Learning Outcomes

At the end of this lecture, you’ll be able to:

- Define what a
**Linked List**is. - Enumerate the advantages & disadvantages of a linked list based vs. an array-based implementation.
**Trace**the basic operations of a (singly) linked-list implementation.- Understand the basic operations of a (singly) linked-list well enough to
**implement**them. - Implement an iterator for a (singly) linked-list.
- Appreciate that members of a
**static nested class**(Node for Linked List) can be accessed by the outer class, but not the other way around. - Implement
`IndexedList`

operations with a (singly) linked list implementation (`LinkedIndexedList`

).

## Lecture Plan

In this lecture, we'll cover the following lessons:

- Array: A Static Data Structure
- Linked List: A Dynamic Data Structure
- Java Interlude: Static Nested Class
- Array vs. Linked List
- Build a Linked List⚡
- Linked List: Follow the References!⚡
- Linked List Operation: Prepend⚡
- Linked List Operation: Traverse⚡
- Linked List Operation: Get⚡
- Linked List Operation: Append⚡
- Linked List Operation: Insert⚡
- Linked List Operation: Delete⚡
- Linked List Iteration: The Iterator!⚡
- Exercise: LinkedIndexedList⚡

Lessons marked with ⚡ contain exercise/activity.

## Downloads

## Array: A Static Data Structure ↗

Array is the most fundamental data structure built into many programming languages. It is also used to implement many other data structures. Indeed, our first choice was to use an array to implement IndexedList ADT.

Arrays have their limitations too; they are static structures and therefore cannot be easily extended or reduced to fit the data set. It is also expensive to insert/delete from the front or middle of an array.

In this lecture, we explore another fundamental data structure called Linked List that does not have the limitations of arrays.

## Linked List: A Dynamic Data Structure ↗

A linked list is a linear data structure where each element is a separate object made of at least two items: the data and a reference to the next element. Conventionally, each element of a Linked List is called a **node**.

```
public class Node<E> {
private E data;
private Node<E> next;
// we can have constructors, setters, getters, etc.
}
```

Here is a minimal implementation for a Linked List:

```
public class LinkedList<T> {
private Node<T> head;
// we can have constructors, methods to add/remove nodes, etc.
}
```

- The entry point into a linked list is called the
`head`

of the list. - The
`head`

is not a separate node, but the reference to the first node. - The last node has a reference to
`null`

. - If the list is empty then the
`head`

is a`null`

reference.

## Resource

Wikipedia’s entry on Linked List is great!

## Java Interlude: Static Nested Class ↗

It is a common practice to nest the `Node`

class inside the `LinkedList`

class:

```
public class LinkedList<T> {
private Node<T> head;
private static class Node<E> {
E data;
Node<E> next;
}
// we can have constructors, methods to add/remove nodes, etc.
}
```

Note the nested class is declared as `static`

.

A static nested class does not have access to the instance members of the outer class.

On the other hand, the outer class has access to the instance members of objects of the static nested class. This is the desired arrangement: the `Node`

does not need access to the members of `LinkedList`

, but `LinkedList`

can access `data`

and `next`

on objects of `Node`

thus eliminate the need for getters/setters.

## Inner vs Static Nested Class

Inner Class | Static Nested Class |
---|---|

Instance member of the outer class (not static!) and as such have access to all the members of the outer class (private, instance, static, etc.) | Static (class) member of the outer class and do not have access to instance members. Rather, the outer class has access to the nested class members for convenience. |

Here is a toy example that showcases the use of inner vs. static nested classes.

## Resources

- Oracle’s Java Tutorial, Nested Classes
- Baeldung’s article Nested Classes in Java
- Beginner’s Book entry on Java’s Inner Class

## Array vs. Linked List ↗

Array is a static data structure. The length of an array is set when the array is created. After creation, its length is fixed. A linked list is a dynamic data structure. The length (number of nodes in a list) is not fixed and can grow/shrink on demand.

Insertion/deletion to the front or at the middle of an array is expensive; it requires shifting other elements to make/fill a gap. Insertion/deletion in a Linked List can be done as cheap as updating a few reference variables (we will see this soon).

One disadvantage of a linked list is that it does not allow direct access to the individual elements. If you want to access a particular item then you have to start at the `head`

and follow the references until you get to that item.

Another disadvantage of a linked list is that it uses more memory compared to an array (to store a reference to the next node, for each element).

## Resource

- Interviewbit’s article on Arrays vs Linked Lists
- Towards Data Science’s article on Arrays vs Linked Lists
- A detailed article (with nice visualization) on Array vs Linked List Data Structures from gitconnected.
- Difference between Linked List and Arrays has a nice table for comparison.

## Build a Linked List ↗

Consider the following implementation of `Node`

(with package private visibility modifier):

```
class Node {
int data;
Node next;
Node(int data) {
this.data = data;
this.next = null; // we can eliminate this line; it happens by default
}
}
```

Exercise Draw a schematic representation of a linked list pointed to by the `head`

variable after the following code snippet has been executed.

```
Node head = new Node(7);
head.next = new Node(5);
head.next.next = new Node(10);
Node node = new Node(9);
node.next = head;
head = node;
```

## Solution

## Linked List: Follow the References! ↗

Consider the following linked list:

Exercise In each case, draw a schematic representation of the linked list after the statement is executed. For each statement, start with the linked list displayed above.

`head = head.next;`

## Solution

`head.next = head.next.next;`

## Solution

`head.next.next.next.next = head;`

## Solution

## Linked List Operation: Prepend ↗

Suppose we have the following linked list and we want to add a new node to the front of it.

Exercise Complete the implementation of the `addFirst`

method that creates a node and adds it to the front of the list.

```
public void addFirst (T data) {
// TODO Implement Me!
}
```

Hint: Use the following visualization as a guidance:

## Solution

```
public void addFirst(T t) {
Node<T> node = new Node<>(t);
// node.next = null; // no need: done by default!
node.next = head;
head = node;
}
```

## Linked List Operation: Traverse ↗

Suppose we have a linked list with $n$ elements (nodes) and we want to go over every element and print out the data stored in it.

Exercise Complete the implementation of `traverse`

that iterates over a linked list and prints the data stored at every node.

```
public void traverse() {
// TODO Implement me!
}
```

Hint: the front of the linked list is marked by the `head`

pointer. If you were to follow the `next`

references, how would you know when you reached the last node?

## Solution

```
public void traverse() {
Node<T> current = head;
while (current != null) {
System.out.println(current.data);
current = current.next;
}
}
```

If you keep count of the number of nodes in the linked list, then you can also write this with a counter-controlled loop.

```
public void traverse() {
Node<T> current = head;
for (int count = 0; count < numElements; count++) {
System.out.println(current.data);
current = current.next;
}
}
```

## Linked List Operation: Get ↗

Suppose we have a linked list with $n$ elements (nodes) and we want to get the data stored in the $k^{th}$ element (at index $k-1$).

Exercise Complete the implementation of the `get`

method which returns data stored at a given index.

```
public T get(int index) {
return null; // TODO Implement me!
}
```

Hint: you cannot directly jump to $K^{th}$ node. You need to start at the `head`

and follow the `next`

references to *get there*!

## Solution

```
public T get(int index) {
return find(index).data;
}
// PRE: 0 <= index < numElements
private Node<T> find(int index) {
Node<T> target = head;
for(int counter = 0; counter < index; ounter++) {
target = target.next;
}
return target;
}
```

**Caution**: the implementation above fails to account for an edge case!

## Linked List Operation: Append ↗

Suppose we have the following linked list and we want to append (add to the end) a new node.

Exercise Complete the implementation of the `addLast`

method that creates a node and add it to the back of the list.

```
public void addLast(T t) {
// TODO Implement me!
}
```

Hint: Use the following visualization as guidance.

## Solution

```
public void addLast(T t) {
Node<T> tail = head;
while (tail.next != null) {
tail = tail.next;
}
tail.next = new Node<T>(t);
}
```

If you keep count of the number of nodes in the linked list, then you can also write this with a counter-controlled loop. In that case, the `find`

helper method from when we implemented `get`

can be used here to go to the last element.

**Caution**: the implementation above fails to account for an edge case!

## Linked List Operation: Insert ↗

Suppose we have a linked list with $n$ elements (nodes) and we want to insert a new node at index $k$. This means we insert a new node between elements at indices $k-1$ and $k$. After the insertion, we will have $n + 1$ elements:

- The node which was at index $k - 1$ before the insertion will remain at that index after the insertion.
- The node at index $k$ before the insertion will be at index $k+1$ after the insertion.
- The newly added node will be at index $k$.

Example: we have a linked list with 3 nodes at indices `0`

, `1`

and `2`

.

We will insert a new node at index `2`

:

Exercise Complete the implementation of `insert`

which adds a new node at the given index.

```
public void insert(int index, T t) {
// TODO Implement me!
}
```

Hint: Use the following visualization as guidance.

## Solution

```
public void insert(int index, T t) {
Node<T> target = head;
for (int counter = 0; counter < index; ounter++) {
target = target.next;
}
Node<T> node = new Node(t);
node.next = target.next;
target.next = node;
}
```

**Caution**: the implementation above fails to account for edge cases or cases where `index`

is invalid!

## Linked List Operation: Delete ↗

Suppose we have a linked list with $n$ elements (nodes) and we want to delete an element at a given index $k$. This means we remove the $k^{th}$ node from the list. After deletion we have $n - 1$ elements:

- The node which was at index $k - 1$ before the deletion will remain at that index after the deletion.
- The nodes at index $k + i$ for $i \in [1, n-1-k ]$ before the deletion will be at index $k+ j$ after the deletion for $j \in [0, n-2-k]$.

Example: we have a linked list with 4 nodes at indices `0`

, `1`

, `2`

, and `3`

.

We will delete the node at index `2`

:

Exercise Complete the implementation of `delete`

which removes a node at the given index.

```
public void delete(int index) {
// TODO Implement me!
}
```

Hint: Use the following visualization as guidance.

## Solution

```
public void delete(int index) {
Node<T> beforeTarget = head;
for(int counter = 0; counter < index - 1; counter++) {
beforeTarget = beforeTarget.next;
}
beforeTarget.next = beforeTarget.next.next;
}
```

**Caution**: the implementation above fails to account for edge cases and cases where `index`

is invalid!

## Linked List Iteration: The Iterator! ↗

Assume we pass the `head`

reference variable which points to the front of a linked list to the following Iterator class.

Exercise Complete the implementation of `hasNext`

and `next`

methods.

```
public class LinkedList<T> implements Iterable<T> {
private Node<T> head;
// other fields/methods are not shown here!
@Override
public Iterator<T> iterator() {
return new LinkedListIterator();
}
private class LinkedListIterator implements Iterator<T> {
@Override
public T next() {
return null; // TODO Implement me!
}
@Override
public boolean hasNext() {
return false; // TODO Implement me!
}
}
}
```

Hint: describe the responsibilities of `hasNext`

and `next`

before implementing them.

## Solution

```
private class LinkedListIterator implements Iterator<T> {
private Node<T> current;
public LinkedListIterator(Node<T> head) {
current = head;
}
@Override
public T next() {
if (!hasNext()) {
throw new NoSuchElementException();
}
T t = current.data;
current = current.next;
return t;
}
@Override
public boolean hasNext() {
return current != null;
}
}
```

## Exercise: LinkedIndexedList ↗

Open the starter code and look for the `LinkedIndexedList.java`

file. This file is a linked list implementation of IndexedList ADT.

Exercise Complete the implementation of `LinkedIndexedList`

(at home!)

Note here we __cannot__ start with an empty list. Instead, we must build a complete list (with the given size and the default value) in the constructor of the `LinkedIndexedList`

.

We have written our unit tests based on the specification of the IndexedList ADT. We can therefore use the same suite of tests to test both implementations of IndexedList (that is `ArrayIndexedList`

and `LinkedIndexedList`

). We need to switch between these two implementations. Open the starter code and find out how this is done by leveraging inheritance. (Hint: look for the abstract method `createUnit`

).

## Solution

Refer to the solution code posted.