We will keep it as static class in the custom linked list class otherwise every Node<E> will store an extra hidden reference to CustomLinkedList<E>, even though it doesn’t need it. Using private static class Node<E> eliminates this unnecessary reference.
2. LinkedList Class
Our CustomLinkedList will maintain:
A head (first node)
A tail (last node)
The size of the list
public class CustomLinkedList<E> {
private Node<E> head;
private Node<E> tail;
private int size;
public CustomLinkedList() {
this.head = null;
this.tail = null;
this.size = 0;
}
}
3. Core Operations
Below are the essential operations for a doubly linked list.
3.1 Add Element at End (addLast)
public void addLast(E data) {
Node<E> newNode = new Node<>(data);
if (head == null) { // Empty list
head = tail = newNode;
} else {
tail.next = newNode;
newNode.prev = tail;
tail = newNode;
}
size++;
}
3.2 Add Element at Beginning (addFirst)
public void addFirst(E data) {
Node<E> newNode = new Node<>(data);
if (head == null) {
head = tail = newNode;
} else {
newNode.next = head;
head.prev = newNode;
head = newNode;
}
size++;
}
3.3 Remove Element from End (removeLast)
public E removeLast() {
if (tail == null) throw new RuntimeException("List is empty");
E data = tail.data;
if (head == tail) { // Only one element
head = tail = null;
} else {
tail = tail.prev;
tail.next = null;
}
size--;
return data;
}
3.4 Remove Element from Beginning (removeFirst)
public E removeFirst() {
if (head == null) throw new RuntimeException("List is empty");
E data = head.data;
if (head == tail) { // Only one element
head = tail = null;
} else {
head = head.next;
head.prev = null;
}
size--;
return data;
}
3.5 Get Element at Index (get)
public E get(int index) {
if (index < 0 || index >= size) throw new IndexOutOfBoundsException();
Node<E> temp = head;
for (int i = 0; i < index; i++) {
temp = temp.next;
}
return temp.data;
}