본문 바로가기
Go

[Go] Slice

by llHoYall 2021. 10. 16.

Slice is a scalable collection type, unlike Array.

Slice has advantages over Array in terms of memory usage and speed.

Slice Declaration

var slice []string
slice = make([]string, 3)
slice[0] = "hello"
slice[2] = "hi"
fmt.Println(slice[0], slice[1], slice[2])
// hello  hi

numSlice := make([]int, 3)
numSlice[0] = 1
numSlice[1] = 2
numSlice[2] = 3
fmt.Println(numSlice[0], numSlice[1], numSlice[2])
// 1 2 3

There are two ways to declare Slices.

make function makes a Slice.

Zero Initialization

Like Array, Slice elements that do not assign any value are returned with zero values.

intSlice := make([]int, 3)
strSlice := make([]string, 3)
boolSlice := make([]bool, 3)

fmt.Println(intSlice, strSlice, boolSlice)
// [0 0 0] [  ] [false false false]

 

However, slice variables that are not assigned any slices have nil values.

var intSlice []int
var strSlice []string
var boolSlice []bool

fmt.Printf("%#v, %#v, %#v\n", intSlice, strSlice, boolSlice)
// []int(nil), []string(nil), []bool(nil)

Slice Literal

fruits := []string{"apple", "banana", "cherry"}
fmt.Println(fruits)
// [apple banana cherry]

With slice literal, you do not need to use the make function because it automatically generates Slice and set values.

Slice with Loop

It is the same as Array.

numSlice := []int{3, 6, 9}
for i := 0; i < len(numSlice); i++ {
  fmt.Println(i, numSlice[i])
}
// 0 3
// 1 6
// 2 9

for index, value := range numSlice {
  fmt.Println(index, value)
}
// 0 3
// 1 6
// 2 9

Slice Operator

All Slices are implemented based on the underlying Array.

The slice operator (slicing) allows you to create a Slice based on an Array.

numArray := [5]int{1, 2, 3, 4, 5}
//       +---+---+---+---+---+
// index | 0 | 1 | 2 | 3 | 4 |
//       +---+===+===+===+---+
// value | 1 | 2 | 3 | 4 | 5 |
//       +---+===+===+===+---+

numSlice := numArray[1:4]
fmt.Print(numSlice)
// [2 3 4]

[1:4] means getting elements from the 1st index of the Array to the 3rd (before 4th) index of the Array.

numArray := [5]int{1, 2, 3, 4, 5}
	
numSlice := numArray[:3]
fmt.Print(numSlice)
// [1 2 3]

numSlice = numArray[3:]
fmt.Print(numSlice)
// [4 5]

If you omit the starting index, it uses 0 by default.

If you omit the ending index, it uses the last index + 1 by default.

 

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

numSlice1 := numArray[:3]
fmt.Println(numSlice1)
// [1 2 3]

numSlice2 := numArray[2:]
fmt.Println(numSlice2)
// [3 4 5]

Multiple Slices can point to the same underlying Array.

 

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

numSlice := numArray[:3]
fmt.Println(numSlice)
// [1 2 3]

numArray[1] = 6
fmt.Println(numSlice)
// [1 6 3]

numSlice[2] = 7
fmt.Println(numArray)
// [1 6 7 4 5]

If you change the value of the underlying Array, the value of the Slice also changes accordingly, and vice versa.

Because of these potential problems, it is generally better to create Slices using make() or slice literal than to create Array first and use slice operators.

This is because if you use make() or slice literal, you don't need to touch the underlying Array.

Adding Elements to Slice

fruits := []string{"apple", "banana"}

fmt.Println(fruits)
// [apple banana]

fruits = append(fruits, "cherry")
fmt.Println(fruits)
// [apple banana cherry]

fruits = append(fruits, "durian", "eggfruit")
fmt.Println(fruits)
// [apple banana cherry durian eggfruit]

append function returns a new Slice with new elements.

You can append a single element, even multiple elements.

 

The returned Slice shares an underlying array with the original Slice.

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

slice2 := append(slice1, 4)
slice3 := append(slice2, 5)

slice3[0] = 9

fmt.Println(slice2)
// [9 2 3 4]

fmt.Println(slice3)
// [9 2 3 4 5]

Therefore, reassigning the same variable is necessary to avoid this problem.

Sorting Elements in Slice

slice := []int{3, 5, 4, 2, 1}
sort.Ints(slice)
fmt.Println(slice)
// [1 2 3 4 5]

sort package provides sorting methods of fundamental types.

If you provide Len(), Less(), Swap() functions, you can sort your own type, such as structure.

package main

import (
  "fmt"
  "sort"
)

type person struct {
  name string
  age  int
}
type persons []person

func (p persons) Len() int {
  return len(p)
}

func (p persons) Less(i, j int) bool {
  return p[i].age < p[j].age
}

func (p persons) Swap(i, j int) {
  p[i], p[j] = p[j], p[i]
}

func main() {
  p := persons{
    {"kim", 31},
    {"lee", 18},
    {"park", 23},
  }
  sort.Sort(persons(p))
  fmt.Println(p)
  // [{lee 18} {park 23} {kim 31}]
}

Structure of Slice

Slice is saved internally like the following structure.

type SliceHeader struct {
  Data uintptr // Pointer to array
  Len  int // Number of elements
  Cap  int // Length of array
}

For example, let's assume this array.

[ 0 | 1 | 2 | - | - ]

In this case, Len is 3 and Cap is 5.

slice := make([]int, 3, 5)
slice[0] = 0
slice[1] = 1
slice[2] = 2
fmt.Println(slice, len(slice), cap(slice))
// [0 1 2] 3 5

 

And, you should focus on the pointer.

It makes a difference from an array.

'Go' 카테고리의 다른 글

[Go] Interface  (0) 2021.10.17
[Go] Method  (0) 2021.10.17
[Go] Package  (0) 2021.10.16
[Go] Rune and String  (0) 2021.10.16
[Go] Pointer  (0) 2021.10.11

댓글