[Golang] Slice

2021-11-09 hit count image

Let's see what the Slice is and how to use it in Golang.

Outline

In this blog post, I will introduce what the Slice is and how to use the Slice in Golang. You can see the full source code of this blog post on the link below.

Slice

Slice is the dynamic array type(the pointer to the array) that Golang providers.

  • Static: Determined at compile time.
  • Dynamic: Determined at runtime.

Next is how to define the array in Golang.

var v [10]int

You can define Slice without setting the size of the array like the below.

var v []int

To check this, create the main.go file and modify it like the below.

package main

import "fmt"

func main() {
  var a [2]string
  var b []string

  fmt.Println(a)
  fmt.Println(b)
}

When you execute the code, you can see the following result.

# go run main.go
[ ]
[]

Array and Slice

Slice is the pointer type to the array. To check this, modify the main.go file like the below.

package main

import "fmt"

func changeArr(arr2 [5]int) {
  arr2[0] = 100
}
func changeSlice(slice2 []int) {
  slice2[0] = 100
}

func main() {
  arr := [5]int{1, 2, 3, 4, 5}
  slice := []int{1, 2, 3, 4, 5}

  changeArr(arr)
  changeSlice(slice)

  fmt.Println(arr)
  fmt.Println(slice)
}

When the code is executed, the following reult is shown.

# go run main.go
[1 2 3 4 5]
[100 2 3 4 5]

Array is not a pointer type, so when the changeArr is called, the new instance called arr2 is created, and then arr is copied to it. So, when arr2 is changed in the changeArr fucntion, the arr is not chagned.

However, Slice is a pointer type. So, when the changeSlice function is called, slice2 gets slice memory address instead of creating a new instance. So, when slice2 is changed in the changeSlice function, the slice is also changed.

len and cap

Slice has len and cap data unlike Array(Array has only len). The len of Slice is the number of elements in the Slice, and the cap is the number of elements in the Slice that can be stored without allocating more memory.

To check this, modify the main.go file like the below.

package main

import "fmt"

func main() {
  slice1 := []int{1, 2, 3, 4, 5}
  slice2 := make([]int, 2, 10)

  fmt.Printf("slice1(%p): len=%d cap=%d %v\n", slice1, len(slice1), cap(slice1), slice1)
  fmt.Printf("slice2(%p): len=%d cap=%d %v\n", slice2, len(slice2), cap(slice2), slice2)
}

When the code is executed, the following result is shown.

# go run main.go
slice1(0xc0000b2000): len=5 cap=5 [1 2 3 4 5]
slice2(0xc0000b4000): len=2 cap=10 [0 0]

make function

You can create the Slice with the make function that Golang provides.

slice1 := make([]int, 10)
fmt.Println(slice1)

When you define the Slice like above with the make function, the 10 size Slice is created.

slice2 := make([]int, 2, 10)
fmt.Println(slice2)

Also, you can create the Slice with the make function with the len and cap parameters. When you define the Slice like the above, the 2 len and 10 cap size Slice is created.

To check this, modify the main.go file like the below.

package main

import "fmt"

func main() {
  slice1 := make([]int, 10)
  fmt.Println(slice1)

  slice2 := make([]int, 2, 10)
  fmt.Println(slice2)
}

When you execute the code, you can see the following result.

# go run main.go
[0 0 0 0 0 0 0 0 0 0]
[0 0]

Slicing

Creating Slice by cutting part of an array is called Slicing.

  • Array => Slicing => Slice

In Golang, you can use slicing like the below.

Array[startIndex:endIndex]

The slicing above returns the value from startIndex to just before endIndex(endIndex -1). At this time, the returned Slice cap is the length from startIndex to the end of Array.

To check this, modify the main.go file like the below.

package main

import "fmt"

func main() {
  array := [5]int{1, 2, 3, 4, 5}
  slice := array[1:2]

  fmt.Printf("array: len=%d %v\n", len(array), array)
  fmt.Printf("slice: len=%d cap=%d %v\n", len(slice), cap(slice), slice)
}

When you execute the code above, you can see the following result.

# go run main.go
array: len=5 [1 2 3 4 5]
slice: len=1 cap=4 [2]

You can also slice the Slice to create a new Slice. Also, you can slice from the first by setting 0 to startIndex, and you can omit 0.

slice = []int{1, 2, 3, 4, 5}
slice1 := slice[0:3]
slice2 := slice[:3]

You can slice to the end, and you can omit the last index like the below.

slice = []int{1, 2, 3, 4, 5}
slice1 = slice[2:len(slice)]
slice2 = slice[2:]

Lastly, you can slice all, and this is normally used for converting Array to Slice.

arr := [5]int{1, 2, 3, 4, 5}
slice := arr[:]

Slicing doesn’t create a new variable, just makes a pointer, so you can use it like the below.

array1 := [100]int{1: 1, 2: 2, 99: 100}
slice1 = array1[1:10]
slice2 = slice1[2:99]

fmt.Println(slice1)
fmt.Println(slice2)

As you see, the slice1 has the memory address of array, so it is possible to create slice2 by taking the values from slice1 to the 99th.

Slicing with cap size

When you slice Array, the returned Slice has the cap from startIndex to the end of Array. However, when you slice Array with maxIndex like the below, you can control the cap size.

slice[startIndex:endIndex:maxIndex]
slice1 = []int{1, 2, 3, 4, 5}
slice2 = slice1[1:3:4]

fmt.Printf("slice1: len=%d cap=%d %v\n", len(slice1), cap(slice1), slice1)
fmt.Printf("slice2: len=%d cap=%d %v\n", len(slice2), cap(slice2), sl

append function

When you want to add an element to the end of the Slice, you can use the append function. When you use the append function, a new Slice is returned.

var slice1 = []int{1, 2, 3}
slice2 := append(slice1, 4)

At this time, the new Slice has the same memory address as the old Slice, or not.

When you use the append function to add an element and the target Slice has enough space to store the element, the new Slice uses the old Slice memory address. However, when the target Slice doesn’t have enough space, the append function copies the old Slice to the new memory address and adds the element to it.

To check this, modify the main.go file like the below.

package main

import "fmt"

func main() {
  slice1 := make([]int, 3)
  slice2 := append(slice1, 4)

  fmt.Println("[New splice]")
  fmt.Printf("slice(%p): len=%d cap=%d %v\n", slice1, len(slice1), cap(slice1), slice1)
  fmt.Printf("slice2(%p): len=%d cap=%d %v\n", slice2, len(slice2), cap(slice2), slice2)

  fmt.Println("slice1 changed ========================================================")
  slice1[0] = 100
  fmt.Printf("slice(%p): len=%d cap=%d %v\n", slice1, len(slice1), cap(slice1), slice1)
  fmt.Printf("slice2(%p): len=%d cap=%d %v\n", slice2, len(slice2), cap(slice2), slice2)

  fmt.Println("slice2 changed ========================================================")
  slice2[0] = 200
  fmt.Printf("slice(%p): len=%d cap=%d %v\n", slice1, len(slice1), cap(slice1), slice1)
  fmt.Printf("slice2(%p): len=%d cap=%d %v\n", slice2, len(slice2), cap(slice2), slice2)

  slice1 = make([]int, 1, 3)
  slice2 = append(slice1, 4)

  fmt.Println("[Same slice]")
  fmt.Printf("slice(%p): len=%d cap=%d %v\n", slice1, len(slice1), cap(slice1), slice1)
  fmt.Printf("slice2(%p): len=%d cap=%d %v\n", slice2, len(slice2), cap(slice2), slice2)

  fmt.Println("slice1 changed ========================================================")
  slice1[0] = 100
  fmt.Printf("slice(%p): len=%d cap=%d %v\n", slice1, len(slice1), cap(slice1), slice1)
  fmt.Printf("slice2(%p): len=%d cap=%d %v\n", slice2, len(slice2), cap(slice2), slice2)

  fmt.Println("slice2 changed ========================================================")
  slice2[0] = 200
  fmt.Printf("slice(%p): len=%d cap=%d %v\n", slice1, len(slice1), cap(slice1), slice1)
  fmt.Printf("slice2(%p): len=%d cap=%d %v\n", slice2, len(slice2), cap(slice2), slice2)
}

When the code is executed, you can see the following result.

# go run main.go
[New splice]
slice(0xc00012a000): len=3 cap=3 [0 0 0]
slice2(0xc00012c000): len=4 cap=6 [0 0 0 4]
slice1 changed ========================================================
slice(0xc00012a000): len=3 cap=3 [100 0 0]
slice2(0xc00012c000): len=4 cap=6 [0 0 0 4]
slice2 changed ========================================================
slice(0xc00012a000): len=3 cap=3 [100 0 0]
slice2(0xc00012c000): len=4 cap=6 [200 0 0 4]
[Same slice]
slice(0xc00012a018): len=1 cap=3 [0]
slice2(0xc00012a018): len=2 cap=3 [0 4]
slice1 changed ========================================================
slice(0xc00012a018): len=1 cap=3 [100]
slice2(0xc00012a018): len=2 cap=3 [100 4]
slice2 changed ========================================================
slice(0xc00012a018): len=1 cap=3 [200]
slice2(0xc00012a018): len=2 cap=3 [200 4]

When the Slice has enough cap size to store the elements, you can see that the new Slice has the same memory address as the old Slice. Therefore, when you use the append function, you should care about that the old Slice and new slice can be changed at the same time.

Copy Slice

You can use the slicing to copy the Slice.

slice1 = []int{1, 2, 3, 4, 5}
slice2 = slice1[:]

slice2[1] = 100

fmt.Println(slice1)
fmt.Println(slice2)

However, the slicing just creates a new Slice pointer instead of creating a new Slice, so when you modify the slice2 value like above, the slice value is also changed.

To solve this issue, you can make a new slice2 that has the same size of the slice, and loop it to copy the elements.

slice1 = []int{1, 2, 3, 4, 5}
slice2 = make([]int, len(slice1))

for i, v := range slice1 {
  slice2[i] = v
}

slice2[1] = 100

fmt.Println(slice1)
fmt.Println(slice2)

Also, you can use the append function to create a new slice, and add all elements to it.

slice1 = []int{1, 2, 3, 4, 5}
slice2 = append([]int{}, slice1...)

slice2[1] = 100

fmt.Println(slice1)
fmt.Println(slice2)

Lastly, you can use the make function to create a slice with the same size of the slice1, and use copy function to copy the elements.

slice1 = []int{1, 2, 3, 4, 5}
slice2 = make([]int, len(slice1))

copy(slice2, slice1)

slice2[1] = 100

fmt.Println(slice1)
fmt.Println(slice2)

In Golang, you can use the copy function like the below.

copy(dst, src)

Delete

You can delete a specific element in the Slice like the below.

slice := []int{1, 2, 3, 4, 5, 6}
deleteIdx := 2

fmt.Println(slice)
for i := deleteIdx + 1; i < len(slice); i++ {
  slice[i-1] = slice[i]
}
slice = slice[:len(slice)-1]
fmt.Println(slice)

Also, you can use the append function to delete the element.

slice = []int{1, 2, 3, 4, 5, 6}
deleteIdx = 2

fmt.Println(slice)
slice = append(slice[:deleteIdx], slice[deleteIdx+1:]...)
fmt.Println(slice)

Lastly, you can use the copy function to delete the element.

slice = []int{1, 2, 3, 4, 5, 6}
deleteIdx = 2

fmt.Println(slice)
copy(slice[deleteIdx:], slice[deleteIdx+1:])
slice = slice[:len(slice)-1]
fmt.Println(slice)

Insert element

In Golang, you can use the for loop to insert an element to the Slice like the below.

slice := []int{1, 2, 3, 4, 5, 6}
insertIdx := 2

fmt.Println(slice)
slice = append(slice, 0)
for i := len(slice) - 2; i >= insertIdx; i-- {
  slice[i+1] = slice[i]
}
slice[insertIdx] = 100
fmt.Println(slice)

Also, you can use the append function to insert the element.

slice = []int{1, 2, 3, 4, 5, 6}
insertIdx = 2

fmt.Println(slice)
slice = append(slice[:insertIdx], append([]int{100}, slice[insertIdx:]...)...)
fmt.Println(slice)

Lastly, you can use the copy function to insert the element.

slice = []int{1, 2, 3, 4, 5, 6}
insertIdx = 2

fmt.Println(slice)
slice = append(slice, 0)
copy(slice[insertIdx+1:], slice[insertIdx:])
slice[insertIdx] = 100
fmt.Println(slice)

Slice sorting

You can sort the Slice by the sort package provided by the Golang.

package main

import (
  "fmt"
  "sort"
)

func main() {
  slice := []int{6, 3, 1, 5, 4, 2}

  fmt.Println(slice)
  sort.Ints(slice)
  fmt.Println(slice)
}

Also, you can sort the structure Slice with the sort package like the below.

package main

import (
  "fmt"
  "sort"
)

type Student struct {
  Name string
  Age  int
}

type Students []Student

func (s Students) Len() int           { return len(s) }
func (s Students) Less(i, j int) bool { return s[i].Age < s[j].Age }
func (s Students) Swap(i, j int)      { s[i], s[j] = s[j], s[i] }

func main() {
  students := []Student{
    {"c", 31},
    {"a", 20},
    {"b", 21},
    {"d", 19},
  }

  fmt.Println(students)
  sort.Sort(Students(students))
  fmt.Println(students)
}

Completed

Done! we’ve seen how to define Slice and how to use it in Golang. Also, we’ve seen the difference between Array and Slice, and we’ve learned how to create Slice by slicing the Array.

Was my blog helpful? Please leave a comment at the bottom. it will be a great help to me!

Posts