We were given an assignment to create a LinkedList from scratch, and there are absolutely no readings given to guide us on this migrane-causing task. Also everything online
How about a fully functional implementation of a non-recursive Linked List?
I created this for my Algorithms I class as a stepping stone to gain a better understanding before moving onto writing a doubly-linked queue class for an assignment.
Here's the code:
import java.util.Iterator;
import java.util.NoSuchElementException;
public class LinkedList implements Iterable {
private Node first;
private Node last;
private int N;
public LinkedList() {
first = null;
last = null;
N = 0;
}
public void add(T item) {
if (item == null) { throw new NullPointerException("The first argument for addLast() is null."); }
if (!isEmpty()) {
Node prev = last;
last = new Node(item, null);
prev.next = last;
}
else {
last = new Node(item, null);
first = last;
}
N++;
}
public boolean remove(T item) {
if (isEmpty()) { throw new IllegalStateException("Cannot remove() from and empty list."); }
boolean result = false;
Node prev = first;
Node curr = first;
while (curr.next != null || curr == last) {
if (curr.data.equals(item)) {
// remove the last remaining element
if (N == 1) { first = null; last = null; }
// remove first element
else if (curr.equals(first)) { first = first.next; }
// remove last element
else if (curr.equals(last)) { last = prev; last.next = null; }
// remove element
else { prev.next = curr.next; }
N--;
result = true;
break;
}
prev = curr;
curr = prev.next;
}
return result;
}
public int size() {
return N;
}
public boolean isEmpty() {
return N == 0;
}
private class Node {
private T data;
private Node next;
public Node(T data, Node next) {
this.data = data;
this.next = next;
}
}
public Iterator iterator() { return new LinkedListIterator(); }
private class LinkedListIterator implements Iterator {
private Node current = first;
public T next() {
if (!hasNext()) { throw new NoSuchElementException(); }
T item = current.data;
current = current.next;
return item;
}
public boolean hasNext() { return current != null; }
public void remove() { throw new UnsupportedOperationException(); }
}
@Override public String toString() {
StringBuilder s = new StringBuilder();
for (T item : this)
s.append(item + " ");
return s.toString();
}
public static void main(String[] args) {
LinkedList list = new LinkedList<>();
while(!StdIn.isEmpty()) {
String input = StdIn.readString();
if (input.equals("print")) { StdOut.println(list.toString()); continue; }
if (input.charAt(0) == ('+')) { list.add(input.substring(1)); continue; }
if (input.charAt(0) == ('-')) { list.remove(input.substring(1)); continue; }
break;
}
}
}
Note: It's a pretty basic implementation of a singly-linked-list. The 'T' type is a generic type placeholder. Basically, this linked list should work with any type that inherits from Object. If you use it for primitive types be sure to use the nullable class equivalents (ex 'Integer' for the 'int' type). The 'last' variable isn't really necessary except that it shortens insertions to O(1) time. Removals are slow since they run in O(N) time but it allows you to remove the first occurrence of a value in the list.
If you want you could also look into implementing:
Honestly, it only takes a few lines of code to make this a doubly-linked list. The main difference between this and a doubly-linked-list is that the Node instances of a doubly-linked list require an additional reference that points to the previous element in the list.
The benefit of this over a recursive implementation is that it's faster and you don't have to worry about flooding the stack when you traverse large lists.
There are 3 commands to test this in the debugger/console:
If you have never seen the internals of how one of these works I suggest you step through the following in the debugger:
While there are better and more efficient approaches for lists like array-lists, understanding how the application traverses via references/pointers is integral to understanding how many higher-level data structures work.