Core Java

Java Custom Linked List Implementation

Arrays store elements in a contiguous memory block, whereas a linked list spreads its nodes across different memory locations. Each node holds both data and a reference to the next, allowing efficient element insertion without the need for memory reallocation. Hence, let us delve into understanding the internals of a Java linked list, by developing a custom linked list implementation from scratch with functionalities like insertion, deletion, retrieval, and element counting.

1. What is Linked List Data Structure in Java?

A Linked List is a linear data structure where elements (nodes) are linked using pointers. Unlike arrays, linked lists do not require a contiguous memory location. Each node contains:

  • Data: The actual value stored in the node.
  • Pointer: A reference to the next node in the sequence.

Unlike arrays, linked lists provide dynamic memory allocation, making them efficient for insertions and deletions. However, they require additional memory for storing pointers.

1.1 Types of Linked Lists

Linked lists can be categorized into different types based on how nodes are linked together. Each type has its own structure and use case.

  • Singly Linked List: Each node contains data and a pointer to the next node in the sequence.
  • Doubly Linked List: Each node contains data, a pointer to the next node, and a pointer to the previous node, allowing traversal in both directions.
  • Circular Linked List: The last node points back to the first node, forming a circular structure. This can be singly or doubly linked.

1.2 Advantages of Linked Lists

Linked lists offer several benefits over arrays, making them suitable for certain applications where dynamic memory allocation and efficient modifications are required.

  • Efficient insertions and deletions: Unlike arrays, linked lists do not require shifting elements when inserting or deleting elements.
  • Dynamic memory allocation: They grow and shrink as needed, avoiding unnecessary memory usage.
  • Faster reorganization: Data structures like stacks and queues can be implemented efficiently using linked lists.

1.3 Disadvantages of Linked Lists

Despite their advantages, linked lists also have some downsides, primarily due to their pointer-based structure.

  • Extra memory overhead: Each node requires additional space for storing pointers, increasing memory consumption.
  • Slower search operations: Unlike arrays, linked lists do not allow direct access to elements, requiring sequential traversal.
  • More complex implementation: Managing pointers and memory allocation makes linked lists harder to implement compared to arrays.

2. Understanding Linked List via Code

Let’s explore the Singly Linked List and its various implementations through a code example.

001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
// Class representing a Node in the Linked List
class Node {
    int data; // Data stored in the node
    Node next; // Reference to the next node
 
    /**
     * Constructor to initialize a new node with given data.
     * @param data The value to store in the node.
     */
    public Node(int data) {
        this.data = data;
        this.next = null;
    }
}
 
// Class representing the Linked List
class LinkedList {
    private Node head; // Head (first node) of the linked list
 
    /**
     * Inserts a new node at the end of the linked list.
     * @param data The value to insert.
     */
    public void insert(int data) {
        Node newNode = new Node(data);
        if (head == null) {
            head = newNode;
            return;
        }
        Node temp = head;
        while (temp.next != null) {
            temp = temp.next;
        }
        temp.next = newNode;
    }
 
    /**
     * Inserts a new node at the beginning of the linked list.
     * @param data The value to insert.
     */
    public void insertAtBeginning(int data) {
        Node newNode = new Node(data);
        newNode.next = head;
        head = newNode;
    }
 
    /**
     * Inserts a new node at a specific position in the linked list.
     * @param data The value to insert.
     * @param position The position at which to insert the new node (0-based index).
     */
    public void insertAtPosition(int data, int position) {
        Node newNode = new Node(data);
        if (position == 0) {
            insertAtBeginning(data);
            return;
        }
        Node temp = head;
        for (int i = 0; i < position - 1 && temp != null; i++) {
            temp = temp.next;
        }
        if (temp == null) {
            System.out.println("Position out of range.");
            return;
        }
        newNode.next = temp.next;
        temp.next = newNode;
    }
 
    /**
     * Deletes the first occurrence of a node with the given value.
     * @param data The value to delete.
     */
    public void deleteByValue(int data) {
        if (head == null) return;
 
        if (head.data == data) {
            head = head.next;
            return;
        }
 
        Node temp = head;
        while (temp.next != null && temp.next.data != data) {
            temp = temp.next;
        }
 
        if (temp.next == null) {
            System.out.println("Element not found.");
            return;
        }
        temp.next = temp.next.next;
    }
 
    /**
     * Searches for a node with the given value in the linked list.
     * @param key The value to search for.
     * @return true if the value is found, otherwise false.
     */
    public boolean search(int key) {
        Node temp = head;
        while (temp != null) {
            if (temp.data == key) return true;
            temp = temp.next;
        }
        return false;
    }
 
    /**
     * Reverses the linked list.
     */
    public void reverse() {
        Node prev = null, current = head, next;
        while (current != null) {
            next = current.next;
            current.next = prev;
            prev = current;
            current = next;
        }
        head = prev;
    }
 
    /**
     * Displays the linked list elements.
     */
    public void display() {
        Node temp = head;
        while (temp != null) {
            System.out.print(temp.data + " -> ");
            temp = temp.next;
        }
        System.out.println("null");
    }
}
 
// Driver class to test the Linked List implementation
public class LinkedListDemo {
    public static void main(String[] args) {
        LinkedList list = new LinkedList();
 
        // Insert elements
        list.insert(10);
        list.insert(20);
        list.insert(30);
        System.out.println("Initial Linked List:");
        list.display(); // Output: 10 -> 20 -> 30 -> null
 
        // Insert at the beginning
        list.insertAtBeginning(5);
        System.out.println("After inserting 5 at the beginning:");
        list.display(); // Output: 5 -> 10 -> 20 -> 30 -> null
 
        // Insert at a specific position
        list.insertAtPosition(15, 2);
        System.out.println("After inserting 15 at position 2:");
        list.display(); // Output: 5 -> 10 -> 15 -> 20 -> 30 -> null
 
        // Delete a node by value
        list.deleteByValue(20);
        System.out.println("After deleting 20:");
        list.display(); // Output: 5 -> 10 -> 15 -> 30 -> null
 
        // Search for an element
        System.out.println("Searching for 15: " + list.search(15)); // Output: true
 
        // Reverse the linked list
        list.reverse();
        System.out.println("After reversing the linked list:");
        list.display(); // Output: 30 -> 15 -> 10 -> 5 -> null
    }
}

2.1 Code Explanation and Output

This Java program demonstrates various operations on a singly linked list, including insertion, deletion, searching, and reversing the list. A linked list is a dynamic data structure where elements (nodes) are linked together using pointers, making it different from an array, which requires a contiguous memory location.

The program consists of two main classes:

  • Node Class: Represents an individual node in the linked list. Each node contains:
    • Data: Stores the actual value.
    • Next Pointer: Holds the reference to the next node in the list.
  • LinkedList Class: Manages the linked list and provides various methods to perform operations on it. These methods allow for efficient modification of the list without requiring large-scale memory reallocations.

2.1.1 Key Operations

The LinkedList class provides several essential operations to manipulate the data structure efficiently:

  • Insertion Methods: The linked list allows inserting nodes in different ways:
    • insert(int data): Adds a node at the end of the list. This requires traversing the entire list to find the last node and linking it to the new node.
    • insertAtBeginning(int data): Adds a node at the start of the list. This is an efficient operation as it simply updates the head reference to point to the new node.
    • insertAtPosition(int data, int position): Adds a node at a specific position. This requires traversing the list to the desired position and updating pointers accordingly.
  • Deletion Method: The deleteByValue(int data) method removes a node that contains the specified value. It searches for the node and adjusts the references to exclude it from the list.
  • Search Method: The search(int key) method checks if a given value exists in the linked list. It traverses the list sequentially, making it an O(n) operation.
  • Reverse Method: The reverse() method reverses the linked list by rearranging node references. This operation transforms the last node into the new head and updates all pointer connections.
  • Display Method: The display() method prints all the elements of the linked list in sequence. It starts from the head node and follows the next pointers until reaching the end (null).

2.1.2 Code Output

When executed, the code produces the following output:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
Initial Linked List:
10 -> 20 -> 30 -> null
 
After inserting 5 at the beginning:
5 -> 10 -> 20 -> 30 -> null
 
After inserting 15 at position 2:
5 -> 10 -> 15 -> 20 -> 30 -> null
 
After deleting 20:
5 -> 10 -> 15 -> 30 -> null
 
Searching for 15: true
 
After reversing the linked list:
30 -> 15 -> 10 -> 5 -> null

3. Conclusion

This program effectively demonstrates how a linked list works in Java, covering fundamental operations like insertion, deletion, searching, and reversal. Understanding these concepts is essential for mastering data structures in Java.

Yatin Batra

An experience full-stack engineer well versed with Core Java, Spring/Springboot, MVC, Security, AOP, Frontend (Angular & React), and cloud technologies (such as AWS, GCP, Jenkins, Docker, K8).
Subscribe
Notify of
guest


This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Back to top button