Golang DSA Chapter 2 Part 1: Go Basics for Data Structures

Welcome to Chapter 2 of our walkthrough of “Learn Data Structures and Algorithms with Golang” by Bhagvan Kommadi. This is Part 1, where we cover the Go-specific building blocks you need before getting into the heavier data structure topics. Think of this as your toolkit chapter.

Chapter 1 was about what data structures are and how to think about algorithm performance. Now we actually start writing Go code. The book covers arrays, slices, two-dimensional slices, and maps here, and that’s exactly what we’ll walk through.

Arrays: The Simple Starting Point

If you’ve used any programming language before, arrays should feel familiar. An array in Go is a fixed-size collection of elements that all share the same type. Here’s how you declare one:

var arr = [5]int{1, 2, 4, 5, 6}

That gives you an array of 5 integers. Nothing fancy. You can loop through it with a regular for loop:

var i int
for i = 0; i < len(arr); i++ {
    fmt.Println("printing elements", arr[i])
}

Or you can use the range keyword, which gives you both the index and the value:

var value int
for i, value = range arr {
    fmt.Println("range", value)
}

If you don’t need the index, use the blank identifier _ to ignore it:

for _, value = range arr {
    fmt.Println("blank range", value)
}

Here’s the important thing about Go arrays: they are fixed-size. You can’t add more elements than what you declared. If you need a bigger array, you have to create a new one and copy everything over. Also, when you pass an array to a function, Go copies the entire thing. So if you have a huge array, that’s a performance hit.

This is exactly why slices exist.

Slices: Arrays That Can Grow

A slice is basically an array that knows how to resize itself. It sits on top of a regular array but adds the ability to grow when you need more room. When you append elements and the slice runs out of capacity, Go creates a new underlying array (usually double the size) and moves everything over.

Creating a slice looks almost like creating an array, just without the size:

var slice = []int{1, 3, 5, 6}

You can check the length and capacity:

slice = append(slice, 8)
fmt.Println("Capacity", cap(slice))
fmt.Println("Length", len(slice))

The len() function tells you how many elements are in the slice right now. The cap() function tells you how many elements the underlying array can hold before it needs to grow again.

Passing Slices to Functions

Here’s something nice about slices compared to arrays. When you pass a slice to a function, Go passes a reference, not a copy. So modifying the slice inside the function changes the original. No performance penalty for big slices.

func twiceValue(slice []int) {
    var i int
    var value int
    for i, value = range slice {
        slice[i] = 2 * value
    }
}

func main() {
    var slice = []int{1, 3, 5, 6}
    twiceValue(slice)
    var i int
    for i = 0; i < len(slice); i++ {
        fmt.Println("new slice value", slice[i])
    }
}

After calling twiceValue, your original slice has 2, 6, 10, 12 in it. The function modified it in place because slices are passed by reference.

Two-Dimensional Slices

Sometimes you need a grid, like a chessboard or a matrix. Go handles this with two-dimensional arrays and slices.

A fixed two-dimensional array looks like this:

func main() {
    var TwoDArray [8][8]int
    TwoDArray[3][6] = 18
    TwoDArray[7][4] = 3
    fmt.Println(TwoDArray)
}

That gives you an 8x8 grid of integers, all initialized to zero except the two spots you set. It works, but it’s rigid. You can’t resize it.

For something dynamic, you use a slice of slices:

func main() {
    var rows int
    var cols int
    rows = 7
    cols = 9

    var twodslices = make([][]int, rows)
    var i int
    for i = range twodslices {
        twodslices[i] = make([]int, cols)
    }
    fmt.Println(twodslices)
}

The make function creates a slice with the specified number of rows, and then you loop through each row to create the columns. Now you have a 7x9 grid that you built dynamically.

Sub-Slicing and Appending

You can also grab pieces of an existing slice and work with them:

var arr = []int{5, 6, 7, 8, 9}
var slic1 = arr[:3]
fmt.Println("slice1", slic1) // [5 6 7]

var slic2 = arr[1:5]
fmt.Println("slice2", slic2) // [6 7 8 9]

var slic3 = append(slic2, 12)
fmt.Println("slice3", slic3) // [6 7 8 9 12]

The arr[:3] syntax grabs elements from index 0 up to (but not including) index 3. The arr[1:5] grabs from index 1 to 4. And append tacks a new element on the end. If the underlying array is full, Go allocates a bigger one behind the scenes.

Maps: Key-Value Pairs

Maps let you store data as key-value pairs. If you’ve used dictionaries in Python or objects in JavaScript, same idea.

Here’s a map with integer keys and string values:

var languages = map[int]string{
    3: "English",
    4: "French",
    5: "Spanish",
}

You can also create maps with make:

var products = make(map[int]string)
products[1] = "chair"
products[2] = "table"

Looping through a map uses range just like slices:

var i int
var value string
for i, value = range languages {
    fmt.Println("language", i, ":", value)
}

To get a value by key:

fmt.Println(products[2]) // "table"

To delete an entry:

delete(products, 1)
fmt.Println("products", products)

One thing to keep in mind: map iteration order in Go is not guaranteed. If you loop through the same map twice, you might get the keys in a different order each time. That’s by design.

What We Covered

This first half of Chapter 2 gives you the basic Go data types you’ll use throughout the rest of the book:

  • Arrays for fixed-size collections (passed by value, so watch the performance)
  • Slices for dynamic collections (passed by reference, can grow as needed)
  • Two-dimensional slices for grids and matrices
  • Maps for key-value lookups

These are the primitives that everything else builds on. Linked lists, trees, hash tables, they all use some combination of these under the hood.

In Part 2, we’ll look at the rest of Chapter 2, which covers database operations, variadic functions, and CRUD web forms in Go. It gets more practical and shows how these data structures show up in real applications.


This post is part of a series retelling “Learn Data Structures and Algorithms with Golang” by Bhagvan Kommadi (Packt, 2019, ISBN: 978-1-78961-850-1). You can find all posts in the series introduction.

NavigationLink
PreviousChapter 1 Part 2: Algorithms and Performance
NextChapter 2 Part 2: Slices, Maps, and Go Patterns

About

About BookGrill.net

BookGrill.net is a technology book review site for developers, engineers, and anyone who builds things with code. We cover books on software engineering, AI and machine learning, cybersecurity, systems design, and the culture of technology.

Know More