Doubly Linked List in Kotlin: Real-World Use Cases and Code Snippets

Table of Contents

When working with data structures in Kotlin, arrays and lists often come to mind first. They’re built-in, simple, and cover most scenarios. But sometimes you need more control over how elements are connected, inserted, or removed. That’s where a Doubly Linked List in Kotlin shines.

In this blog, we’ll explore what a doubly linked list is, why it’s useful, real-world applications, and most importantly — how to implement one in Kotlin.

Doubly Linked List in Kotlin

A doubly linked list is a data structure made up of nodes. Each node stores three things:

  1. Data — the actual value.
  2. A reference to the next node.
  3. A reference to the previous node.

This dual-link system allows navigation forward and backward through the list. That’s the main difference from a singly linked list, which only moves forward.

Why Use a Doubly Linked List in Kotlin?

You might ask: “Why bother with a doubly linked list when Kotlin already has List and MutableList?”

Here are a few reasons:

  • Fast insertions and deletions: Unlike arrays, you don’t need to shift elements when adding or removing.
  • Bidirectional traversal: You can move in both directions, which can be handy in scenarios like undo/redo features.
  • Custom data structures: Sometimes you want full control over memory and connections.

Real-World Use Cases

Let’s look at where a Doubly Linked List in Kotlin can be practical:

  • Browser history navigation (go back and forward between pages).
  • Undo/Redo operations in editors.
  • Music playlists where you can jump to the previous or next song.
  • Deque (Double-Ended Queue) implementations for efficient queue operations.

Implementing a Doubly Linked List in Kotlin

Let’s write a clean, easy-to-follow implementation.

Define the Node

Java
class Node<T>(
    var data: T,
    var prev: Node<T>? = null,
    var next: Node<T>? = null
)

Here, Node is generic (<T>) so it can store any type (Int, String, custom objects, etc.). Each node keeps track of its data, the previous node (prev), and the next node (next).

Create the DoublyLinkedList Class

Java
class DoublyLinkedList<T> {
    private var head: Node<T>? = null
    private var tail: Node<T>? = null

    fun isEmpty() = head == null
}

We keep track of two references:

  • head → the first node.
  • tail → the last node.

Add Elements

Let’s add items to the end of the list.

Java
fun append(data: T) {
    val newNode = Node(data)

    if (head == null) {
        head = newNode
        tail = newNode
    } else {
        tail?.next = newNode
        newNode.prev = tail
        tail = newNode
    }
}
  • If the list is empty, both head and tail point to the new node.
  • Otherwise, we connect the new node after the current tail and update tail.

Prepend Elements

Adding to the beginning works similarly:

Kotlin
fun prepend(data: T) {
    val newNode = Node(data)

    if (head == null) {
        head = newNode
        tail = newNode
    } else {
        newNode.next = head
        head?.prev = newNode
        head = newNode
    }
}

Remove Elements

Removing a node requires updating both previous and next references.

Kotlin
fun remove(data: T) {
    var current = head

    while (current != null) {
        if (current.data == data) {
            if (current.prev != null) {
                current.prev?.next = current.next
            } else {
                head = current.next
            }

            if (current.next != null) {
                current.next?.prev = current.prev
            } else {
                tail = current.prev
            }
            break
        }
        current = current.next
    }
}

Here we search for the node, reconnect neighbors around it, and update head or tail if needed.

Print the List

For debugging, let’s add a print function.

Kotlin
fun printForward() {
    var current = head
    while (current != null) {
        print("${current.data} ")
        current = current.next
    }
    println()
}

fun printBackward() {
    var current = tail
    while (current != null) {
        print("${current.data} ")
        current = current.prev
    }
    println()
}

Full Example in Action

Before running the code, make sure all the above functions are inside the DoublyLinkedList<T> class.

Kotlin
fun main() {
    val list = DoublyLinkedList<Int>()

    list.append(10)
    list.append(20)
    list.append(30)
    list.prepend(5)

    println("Forward traversal:")
    list.printForward()

    println("Backward traversal:")
    list.printBackward()

    println("Removing 20...")
    list.remove(20)
    list.printForward()
}

Output:

Kotlin
Forward traversal:
5 10 20 30 
Backward traversal:
30 20 10 5 
Removing 20...
5 10 30 

Conclusion

A Doubly Linked List in Kotlin gives you more control when working with dynamic data. While Kotlin’s standard library handles most needs with List or MutableList, knowing how to build and use a doubly linked list can be a powerful skill.

You now know:

  • What a doubly linked list is.
  • Real-world scenarios where it’s useful.
  • How to implement it step by step in Kotlin.

This structure shines in apps where insertion, deletion, or bidirectional navigation matters — like history tracking, playlists, or undo/redo stacks.

Skill Up: Software & AI Updates!

Receive our latest insights and updates directly to your inbox

Related Posts

error: Content is protected !!