Ver en Español
Data Structures in Go: Linked Lists
Jun 24, 2026
Updated: Jun 25, 2026

Data Structures in Go: Linked Lists

Linked lists are a linear, dynamic data structure used to store and organize information in an ordered sequence. Unlike arrays (and, at a lower level, slices backed by an array), linked lists do not have a fixed size, which means they can grow or shrink according to the needs of our program.

In this article, we'll explore the concept of linked lists and how to implement them in Go, a high performance, statically typed programming language developed by Google.

The concept of linked lists

A linked list consists of a series of elements, called nodes, that connect to one another through pointers or references. Each node contains two main parts: the value of the stored data and a pointer to the next node in the list. The last node in the list points to nil, indicating the end of the sequence.

There are several types of linked lists, such as singly linked lists, doubly linked lists, and circular linked lists. The main difference between these types lies in the way the nodes are connected to one another.

  • Singly linked lists: In these lists, each node has a pointer to the next node in the list.
  • Doubly linked lists: Each node has a pointer to the next node and another to the previous node, which makes navigation in both directions easier.
  • Circular linked lists: The last node in the list points to the first node instead of nil, creating a circular loop.

Implementing linked lists in Go

In Go, we can implement linked lists using custom structs and pointers. Below is a basic example of how to implement a singly linked list in Go:

package main

type Node struct {
	data int
	next *Node
}

type LinkedList struct {
	head *Node
	size int
}

func main() {
	// Code to manipulate the linked list
}

In this example, we've defined a Node struct that contains a data field to store the data value and a next field that points to the next node in the list. We've also created a LinkedList struct that contains a head pointer to the first node in the list and a size field that keeps the number of elements in the list.

From this basic implementation, we can add methods to manipulate and manage our linked list, such as inserting, removing, and searching for elements.

The history of linked lists

Linked lists have a long history in the world of computing and date back to the dawn of modern programming. Their invention is attributed to Allen Newell, Cliff Shaw, and Herbert A. Simon, who developed the data structure in the 1950s as part of their work on the RAND Corporation project. Linked lists emerged as a solution to overcome the storage and data manipulation limitations of the computers of that era, which had very limited memory and low performance hardware. Since then, linked lists have become a fundamental pillar in the teaching and application of algorithms and data structures, used across a wide variety of computer systems and programming languages.

Applications of linked lists

Linked lists are a versatile data structure used in many scenarios and applications. Some example uses include:

  • Memory management: Linked lists can be useful when implementing memory management systems, such as keeping track of free memory blocks in an operating system, since they allow adding and removing elements efficiently.
  • Implementing other data structures: Linked lists can serve as the basis for building more complex data structures, such as stacks, queues, priority queues, and trees.
  • Adjacency lists: In graph theory, linked lists can be used to represent adjacency lists, a way of storing graphs in memory. Each node in the linked list represents a vertex of the graph, and its adjacent nodes are stored as a linked list associated with that vertex.
  • Process management: Operating systems use linked lists to keep and manage process lists, which allows adding, removing, and reorganizing processes efficiently.
  • Text manipulation algorithms: Linked lists are useful in text processing applications and algorithms, such as text editors, where inserting and deleting characters or lines happens frequently.
  • Traversals in lists: In certain applications, such as video games or simulations, linked lists can be used to traverse objects and update their state or position in real time.
  • Undo and redo in applications: Doubly linked lists are useful for implementing undo and redo features in software applications, since they allow navigating forward and backward through the history of actions performed.

It's worth noting that, although linked lists are a valuable tool in certain use cases, they are not always the most efficient data structure. In some scenarios, other structures such as arrays, slices, or trees may be more appropriate. Choosing the right data structure will depend on the specific requirements of the application and the operations performed most frequently.

The most common linked list methods

Below is an implementation of a singly linked list in Go with methods for basic operations such as inserting, removing, and searching for elements:

package main

import "fmt"

type Node struct {
	data int
	next *Node
}

type LinkedList struct {
	head *Node
	size int
}

// Insert an element at the end of the linked list
func (list *LinkedList) Append(value int) {
	newNode := &Node{data: value, next: nil}

	if list.head == nil {
		list.head = newNode
	} else {
		current := list.head
		for current.next != nil {
			current = current.next
		}
		current.next = newNode
	}

	list.size++
}

// Remove an element from the linked list by value
func (list *LinkedList) Remove(value int) bool {
	if list.head == nil {
		return false
	}

	if list.head.data == value {
		list.head = list.head.next
		list.size--
		return true
	}

	current := list.head
	for current.next != nil {
		if current.next.data == value {
			current.next = current.next.next
			list.size--
			return true
		}
		current = current.next
	}

	return false
}

// Search for an element in the linked list by value
func (list *LinkedList) Search(value int) *Node {
	current := list.head
	for current != nil {
		if current.data == value {
			return current
		}
		current = current.next
	}

	return nil
}

// Print the linked list
func (list *LinkedList) Print() {
	current := list.head
	for current != nil {
		fmt.Printf("%d ", current.data)
		current = current.next
	}
	fmt.Println()
}

func main() {
	list := LinkedList{}

	list.Append(1)
	list.Append(2)
	list.Append(3)
	list.Append(4)

	fmt.Println("Lista enlazada:")
	list.Print()

	list.Remove(3)
	fmt.Println("Lista enlazada después de eliminar el valor 3:")
	list.Print()

	node := list.Search(2)
	if node != nil {
		fmt.Println("Elemento encontrado:", node.data)
	} else {
		fmt.Println("Elemento no encontrado")
	}
}

This code includes the following methods:

  • Append(): Inserts an element at the end of the linked list.
  • Remove(): Removes an element from the linked list given its value. If the element is found in the list, it removes it and returns true. If it isn't found, it returns false.
  • Search(): Searches for an element in the linked list by its value and returns a pointer to the node found, or nil if it isn't found.
  • Print(): Prints the elements of the linked list in order.

You can adapt and expand this code according to the needs of your application, for example by adding methods to insert elements at a specific position or to remove elements by index.

Singly vs doubly linked

Singly linked lists and doubly linked lists are variants of the linked list data structure, and their main difference lies in the way the nodes are connected to one another. Here is the difference between the two:

  • Singly linked lists: In a singly linked list, each node contains a pointer to the next node in the list. This allows you to traverse the list in a single direction, from start to end. The main advantage of singly linked lists is their simplicity and lower memory usage, since only one pointer per node is needed. However, because you can only navigate in one direction, some operations can be inefficient or complicated to implement.
  • Doubly linked lists: In a doubly linked list, each node has two pointers: one to the next node and another to the previous node. This allows you to traverse the list in both directions, which makes certain operations easier to implement, such as removing nodes or accessing adjacent elements. The main drawback of doubly linked lists is their higher memory usage, since two pointers per node are required, and the greater complexity of pointer manipulation during insert and remove operations.

The next topic we recommend learning is how to implement and use doubly linked lists in Go. By mastering doubly linked lists, you'll be able to handle a more flexible data structure suited to certain use cases, such as implementing random access algorithms, manipulating lists in both directions, or implementing undo and redo features in software applications.

Singly linked list:

[Head] -> [Node 1] -> [Node 2] -> ... -> [Node n] -> nil

Each node in a singly linked list contains a pointer to the next node. The last node in the list points to nil, indicating the end of the list.

Doubly linked list:

[Head] <-> [Node 1] <-> [Node 2] <-> ... <-> [Node n] <-> nil

In a doubly linked list, each node contains two pointers: one to the next node and another to the previous node. The first node in the list has its previous pointer pointing to nil, and the last node has its next pointer pointing to nil.

Conclusion

Linked lists are a flexible, dynamic data structure that offers an alternative to arrays and slices in certain use cases. By implementing linked lists in Go, we can take advantage of its static typing system and its focus on simplicity and performance. Throughout this article, we've presented the basics of linked lists and provided an implementation example in Go. In future posts, we'll explore in detail how to manipulate and optimize linked lists.

In short, linked lists are a fundamental data structure in the world of programming. Whether you're working with singly or doubly linked lists, both offer unique advantages and challenges. By mastering these structures, you'll be able to tackle a wide variety of problems and applications in the field of computing.

Don't forget that, although linked lists are a powerful tool, it's always essential to choose the right data structure for each situation. Keep learning and exploring, and don't hesitate to dive into other topics such as trees, graphs, and algorithms.

Exercises to practice

Below are some exercises so you can practice the Linked List structure in Go and send your solution as a Pull request to this repository: Go Para Principiantes.

Note: verify that the repository URL is correct before publishing.

  1. Insert at the beginning: Implement a Prepend(value int) method that inserts an element at the beginning of the linked list.
  2. Insert at a specific position: Implement an InsertAt(value int, position int) method that inserts an element at a specific position in the linked list. If the position is invalid (negative or greater than the size of the list), the method must return an error.
  3. Remove by index: Implement a RemoveAt(position int) method that removes an element from the linked list given its index. If the position is invalid (negative or greater than or equal to the size of the list), the method must return an error.
  4. Reverse the list: Implement a Reverse() method that reverses the order of the elements in the linked list.
  5. Size of the list: Implement a Size() method that returns the number of elements in the linked list without using a size variable in the LinkedList struct.
  6. Get element by index: Implement a Get(index int) (*Node, error) method that returns a pointer to the node at the specified index. If the index is invalid (negative or greater than or equal to the size of the list), the method must return an error.
  7. Remove duplicates: Implement a RemoveDuplicates() method that removes duplicate elements from an unsorted linked list, keeping only the first occurrence of each value.
  8. Split a linked list: Implement a Split(position int) (*LinkedList, error) method that splits the linked list into two lists at a given position. The original list must contain the elements before the position, and the new list must contain the elements from the position onward. If the position is invalid (negative or greater than the size of the list), the method must return an error.
  9. Find the middle element: Implement a FindMiddle() *Node method that finds and returns a pointer to the node of the middle element of the linked list. If the list has an even number of elements, return the first of the two middle nodes.
  10. Detect cycles: Implement a HasCycle() bool method that determines whether a linked list has a cycle (that is, a node that points to an earlier node instead of nil). The method must return true if there is a cycle, and false otherwise.

These exercises will help you become familiar with the basic and advanced operations of linked lists in Go and deepen your skills for manipulating and managing this data structure.

3-point summary

  1. Linked lists are a linear, dynamic structure where each node holds a piece of data and a pointer to the next one, which lets them grow and shrink as needed.
  2. In Go we implement them with structs and pointers, and from a simple base we can add methods like Append, Remove, Search, and Print.
  3. There are variants (singly, doubly, and circular) and many real world applications; the key is choosing the right data structure for each situation.

That's all. I hope this post is useful to you and that you can apply it to a project you have in mind, or that it simply helped you understand how linked lists work in Go.

Leave me a comment if it helped, if you want to add an opinion, or if you have any questions. And remember, if you liked it, you can also share it using the social links below. I wish you much success on your learning journey.

Sebastian Gomez

Sebastian Gomez

Creador de contenido principalmente acerca de tecnología.

Leave a Reply

0 Comments

Advertisements

Related Posts

Categorias