Implementing Linked Lists in Go
Daniel Hayes
Full-Stack Engineer · Leapcell

Key Takeaways
- Linked lists in Go can be implemented from scratch for deeper understanding or by using Go's
container/list
package. - Manual implementation of a singly linked list involves managing node references for efficient insertion and deletion.
- Go's standard library provides a doubly linked list with convenient methods for common operations.
A linked list is a fundamental data structure in computer science, where each element, known as a node, contains data and a reference (or link) to the next node in the sequence. Unlike arrays, linked lists do not store elements contiguously in memory; instead, each node points to the next, allowing for efficient insertion and deletion operations without reorganizing the entire data structure.
Types of Linked Lists
There are several variations of linked lists:
-
Singly Linked List: Each node contains data and a reference to the next node.
-
Doubly Linked List: Each node contains data, a reference to the next node, and a reference to the previous node.
-
Circular Linked List: The last node points back to the first node, forming a circle.
Implementing a Singly Linked List in Go
Go's standard library provides a container/list
package that implements a doubly linked list. However, understanding how to implement a singly linked list from scratch can provide deeper insights into pointers and memory management in Go.
Defining the Node Structure
Each node in a singly linked list will hold an integer value and a reference to the next node:
type Node struct { data int next *Node }
Defining the Linked List Structure
The linked list itself will maintain a reference to the head node and track its length:
type LinkedList struct { head *Node length int }
Inserting at the Head
To insert a new node at the beginning of the list:
func (l *LinkedList) InsertAtHead(data int) { newNode := &Node{data: data, next: l.head} l.head = newNode l.length++ }
Inserting at the Tail
To insert a new node at the end of the list:
func (l *LinkedList) InsertAtTail(data int) { newNode := &Node{data: data} if l.head == nil { l.head = newNode } else { current := l.head for current.next != nil { current = current.next } current.next = newNode } l.length++ }
Deleting a Node
To delete a node by its value:
func (l *LinkedList) Delete(data int) { if l.head == nil { return } if l.head.data == data { l.head = l.head.next l.length-- return } current := l.head for current.next != nil && current.next.data != data { current = current.next } if current.next != nil { current.next = current.next.next l.length-- } }
Traversing the List
To traverse and print the list:
func (l *LinkedList) PrintList() { current := l.head for current != nil { fmt.Println(current.data) current = current.next } }
Using Go's container/list
Package
For practical applications, Go's container/list
package provides a robust implementation of a doubly linked list:
package main import ( "container/list" "fmt" ) func main() { l := list.New() l.PushBack(1) l.PushBack(2) l.PushFront(0) for e := l.Front(); e != nil; e = e.Next() { fmt.Println(e.Value) } }
This will output:
0
1
2
The container/list
package offers a ready-to-use doubly linked list, which can be more efficient for certain operations due to its bidirectional traversal capabilities.
Conclusion
Implementing linked lists in Go provides valuable experience with pointers and dynamic data structures. While Go's standard library offers a doubly linked list through the container/list
package, understanding the underlying implementation details equips developers with the knowledge to make informed decisions about when and how to use such data structures effectively.
FAQs
Linked lists offer efficient insertions and deletions without reallocating memory.
Create a new node, point its next
to the current head, and update the head to the new node.
When you need a robust, built-in doubly linked list with simplified operations.
We are Leapcell, your top choice for hosting Go projects.
Leapcell is the Next-Gen Serverless Platform for Web Hosting, Async Tasks, and Redis:
Multi-Language Support
- Develop with Node.js, Python, Go, or Rust.
Deploy unlimited projects for free
- pay only for usage — no requests, no charges.
Unbeatable Cost Efficiency
- Pay-as-you-go with no idle charges.
- Example: $25 supports 6.94M requests at a 60ms average response time.
Streamlined Developer Experience
- Intuitive UI for effortless setup.
- Fully automated CI/CD pipelines and GitOps integration.
- Real-time metrics and logging for actionable insights.
Effortless Scalability and High Performance
- Auto-scaling to handle high concurrency with ease.
- Zero operational overhead — just focus on building.
Explore more in the Documentation!
Follow us on X: @LeapcellHQ