Lists, as the name suggests, are list of items. These item can be of any type. We can have list of Integer or list of Double or even we can have list of custom data types. Lists elements are stored differently than arrays which gives it certain advantages over arrays. We will see those in this tutorial.

List Overview

List is pointer based data structure which relies heavily on pointers to the next item. Lists consist of nodes where each node contains the data associated with the type of list and the pointer to the next node element. Again, next node contains the same information, its data and the pointer to next node and so on until the last element. The first element of the list is called the head and similarly the last element is called tail.

This implementation detail has certain advantages and disadvantages over Array based Lists. With this implementation, we cannot access element at a specific index in constant time. We have to traverse the list from beginning or end depending on the type of list to reach to specific index position and then retrieve that element. However, this also gives it an advantage that we can easily add new element or remove an element in constant time regardless of the size of the list.

The lists can be mainly categorized into two main types.

  1. Singly Linked List
  2. Doubly Linked List

You can perform following operations on a list.

  1. retrieve element from the list
  2. insert a new element into the list
  3. delete an element from the list
  4. check if an element occurs in the given list
  5. find out how many elements are in the list

A list can be implemented using two ways. One is using array to store its elements. Another approach uses Node elements. That actually uses linked data structure.

ArrayList implementation

To implement a list using arrays, we can store elements in an array. However, arrays have fixed size. If we exceed that size, we may get ArrayIndexOutOfBoundsException. So, to avoid such situation, we will need to expand our array when we reach end of the list. This operation literally requires creating new array and copying existing elements into the new array of larger size. This is an expensive operation as it requires O(n) time complexity. This is also called dynamic arrays sometimes because they change the size based on number of elements.

With this idea in mind, inserting new element will require just adding new element at the size - 1 index position as long as the array has enough empty spaces. deleting an element can be slightly expensive ope In Java, collections are implemented based on interface structure.

List interface hierarchy

We also have to override some of the methods defined in the Collection interface for standard Java library.

What is Singly Linked List

In singly linked lists, we have single directional connections from head towards the tail. We cannot traverse backwards. Visually it looks like below.

Singly Linked List

Linked List with pointers Singly Linked List

To create singly linked list, we first need a single node which can be defined like below. For the sake of simplicity, I have marked member variables as public.

 1class Node {
 2    public int value;
 3    public Node next;
 4
 5    public Node() {
 6
 7    }
 8
 9    public Node(int value) {
10        this.value = value;
11    }
12}

As you can see, we have two member variables, one of type int to store integer values in this simple linked list and another Node itself to store pointer to the next node.

Now, this List should support several operations.

  1public class SinglyLinkedList {
  2
  3    private Node head;
  4//    private Node tail;
  5    private int size;
  6`
  70
  8
  9
 10
 11`
 12    public SinglyLinkedList() {
 13        this.head = null;
 14//        this.tail = null;
 15        this.size = 0;
 16    }
 17
 18    public SinglyLinkedList(int value) {
 19        Node node = new Node(value);
 20        this.head = node;
 21        this.size = this.size++;
 22    }
 23
 24    public int get(int index) {
 25        if (index < 0 || index >= size) return -1;
 26        Node currentNode = this.head;
 27        for (int i = 0; i < index; i++) {
 28            currentNode = currentNode.next;
 29        }
 30        return currentNode.value;
 31    }
 32
 33    public void insert(int value) {
 34        Node newNode = new Node(value);
 35        if (this.head == null) {
 36            this.head = newNode;
 37//            this.tail = newNode;
 38            this.size++;
 39            return;
 40        }
 41        Node currentNode = this.head;
 42//        this.tail.next = newNode;
 43//        this.tail = newNode;
 44        // without tail use below.
 45        while (currentNode.next != null) {
 46            currentNode = currentNode.next;
 47        }
 48        currentNode.next = newNode;
 49        this.size++;
 50    }
 51
 52    public void insert(int index, int value) {
 53        if (index > size) return;
 54        if (index < 0) index = 0;
 55        this.size++;
 56        Node currentNode = this.head;
 57        for (int i = 0; i < index - 1; i++) {
 58            currentNode = currentNode.next;
 59            System.out.println("after for loop" + Integer.toString(i) + " : " + currentNode);
 60        }
 61
 62        Node newNode = new Node(value);
 63        if (currentNode.next == null) {
 64            newNode.next = null;
 65        } else {
 66            newNode.next = currentNode.next;
 67        }
 68        currentNode.next = newNode;
 69        System.out.println("end of loop");
 70    }
 71
 72    public void prepend(int value) {
 73        this.insert(0, value);
 74    }
 75
 76    public void append(int value) {
 77        this.insert(this.size, value);
 78    }
 79
 80    public void delete(int index) {
 81        if (index < 0 || index >= this.size) return;
 82        this.size--;
 83        if (index == 0) {
 84            this.head = this.head.next;
 85            return;
 86        }
 87        Node currentNode = this.head;
 88        for (int i = 1; i < index; i++) {
 89            currentNode = currentNode.next;
 90        }
 91        currentNode.next = currentNode.next.next;
 92    }
 93
 94    public int size() {
 95        return this.size;
 96    }
 97
 98    public int find(int data) {
 99        Node currentNode = this.head;
100        for (int i = 0; i < this.size; i++) {
101            if (currentNode.value == data)
102                return i;
103            currentNode = currentNode.next;
104        }
105        return -1;
106    }
107
108    // TODO: Implement other methods
109
110    public void print() {
111        Node currentNode = head;
112        if (this.size == 0) {
113            System.out.println("Empty List");
114        }
115        while (currentNode != null) {
116            System.out.printf("%d => ", currentNode.value);
117            currentNode = currentNode.next;
118        }
119        System.out.println();
120    }
121}