Go Base Slices

Keywords: Go less

Section

Slice is a variable-length sequence of elements of the same type.It is a layer of encapsulation based on the type of array.Support automatic expansion

Slice is a reference type

The internal structure of the slice contains address, length, and capacity

var name []T
func main() {
  var a []string               // Declare a String Slice
  var b = []int{}              // Declare an integer slice and initialize it
  var c = []bool{true, false}  // Declare a Boolean slice and initialize it
  // var d = []bool{true, false} //Declare a Boolean slice and initialize
  fmt.Println(a)               // []
  fmt.Println(b)               // []
  fmt.Println(c)               // [true, false]
  fmt.Println(a == nil)        // true
  fmt.Println(b == nil)        // false
  fmt.Println(c == nil)        // false
  // fmt.Println(c == d) //Slice is a reference type, does not support direct comparison, can only be compared with nil
}

Slice length and capacity

Slices have their own length and capacity, using the built-in len() function for length, and the built-in cap() function for capacity

Define slices based on arrays

func main() {
  a := [5]int{55, 66, 77, 88,  99}
  b := a[1:4]    
  fmt.Println(b)                      // [66, 77, 88]
  fmt.Printf("type of b: %T\n", b)    // type of b: []int
  b[0] = 1                            // Slice b modifies elements affecting a
  fmt.Println(a)                      // [55 1 77 88 99]
}

Slice-based re-slicing

func main() {
  a := [...]string{"Beijing", "Shanghai", "Guangzhou", "Shenzhen", "Chengdu", "Xi'an"}
  fmt.Printf("a: %v, type: %T, len: %d, cap: %d\n", a, a, len(a), cap(a))
  b := a[1:3]
  fmt.Printf("b: %v, type: %T, len: %d, cap: %d\n", b, b, len(b), cap(b))
  c := b[1:5]
  fmt.Printf("c: %v, type: %T, len: %d, cap: %d\n", c, c, len(c), cap(c))
}
//a: [Beijing, Shanghai, Guangzhou, Shenzhen, Chengdu, Xi'an], type: [6]string, len: 6, cap: 6
//b: [Guangzhou, Shanghai], type: []string, len: 2, cap: 5
//c: [Guangzhou, Shenzhen, Chengdu, Xi'an], type: []string, len: 4, cap: 4
// For b, len gets the number of elements, cap gets the start-to-end capacity of B
// For c, len gets four elements in slice a, and cap capacity is the capacity from the beginning of C to the end of a

Note: When slicing (b) and re-slicing (c), the index must not exceed the length of the original array, otherwise an error of index out of bounds will occur

Using make() function to construct slices

Creating slices dynamically using the make() function

make([]T, size, cap)
// T: Element type of slice
// size: Number of elements in the slice
// cap: Slice capacity

func main() {
  a := make([]int, 2, 10)
  fmt.Printf("a: %v, len: %d, cap: %d", a, len(a), cap(a))
}
// a: [0 0], len: 2, cap: 10

The nature of slicing

The essence of a slice is an encapsulation of a low-level array, which contains three information: the pointer to the low-level array, the length of the slice (len), and the capacity of the slice (cap).

func main() {
  a := [8]int{0, 1, 2, 3, 4, 5, 6, 7}
  s1 := a[:5]    // len=5, cap=8, cap is the capacity of a, because the starting value of slice a is the starting value of a
  s2 := a[3:6]   // len=3, cap=5 for s2, cap starting from 3 to the end of a
}

Slices cannot be compared directly

You cannot use the ==operator to determine if two slices contain all the same elements

The only valid comparison of slices is with nil

Slices with nil values have no underlying array, and the length and capacity of a slice with nil values are both 0

Note: A slice with length and capacity of 0 is not necessarily nil

var s1 []int         //len(s1)=0; cap(s1)=0; s1==nil
s2 := []int{}        //len(s2)=0; cap(s2)=0; s2!=nil
s3 := make([]int, 0) //len(s3)=0; cap(s3)=0; s3!=nil

To determine if a slice is empty, len(s) == 0 should not be used to determine s == nil

Assignment copy of slice

func main() {
 s1 := make([]int, 5)  // [0, 0, 0, 0, 0]
 s2 := s1[2:]          // Share underlying array
 s2[0] = 100
 fmt.Println(s1)       // [0 0 100 0 0]
 fmt.Println(s2)       // [100 0 0]
 
}

Note: Modifications to one slice affect the contents of another slice

Traversal of slices

The slice traversal is consistent with the array and can be traversed by index and for range

func main() {
  s := []int{1, 3, 5}
  for i := 0; i < len(s); i++ {
    fmt.Println(i, s[i])
  }
  for index, value := range s {
    fmt.Println(index, value)
  }
}

The append method adds elements to the slice

  • Each slice points to a low-level array that has enough capacity to add new elements
  • When the underlying array cannot accommodate new elements, the slice will automatically expand according to a certain strategy, and the underlying array that the slice points to will be replaced
  • Expansion often occurs in append() function calls, so we usually need to use the original variable to receive the return value of the append function
func main() {
  // append() add elements and slice expansion
  var numSlice []int
  for i := 0; i < 10; i++ {
    numSlice = append(numSlice, i)
    fmt.Printf("%v len:%d, cap:%d, ptr:%p\n", numSlice, len(numSlice), cap(numSlice), numSlice)
  }
}
// [0] len:1, cap:1, ptr:0xc000016080
// [0 1] len:2, cap:2, ptr:0xc0000160c0
// [0 1 2] len:3, cap:4, ptr:0xc000018180
// [0 1 2 3] len:4, cap:4, ptr:0xc000018180
// [0 1 2 3 4] len:5, cap:8, ptr:0xc000012080
// [0 1 2 3 4 5] len:6, cap:8, ptr:0xc000012080
// [0 1 2 3 4 5 6] len:7, cap:8, ptr:0xc000012080
// [0 1 2 3 4 5 6 7] len:8, cap:8, ptr:0xc000012080
// [0 1 2 3 4 5 6 7 8] len:9, cap:16, ptr:0xc000078000
// [0 1 2 3 4 5 6 7 8 9] len:10, cap:16, ptr:0xc000078000

Visible to the naked eye: slice numSlice automatically expands according to the rules of 1, 2, 4, 8, 16, twice as large as before each expansion

The append() function also supports appending multiple elements at once

func main() {
  var citySlice []string
  citySlice = append(citySlice, "Beijing")
  citySlice = append(citySlice, "Shanghai", "Guangzhou", "Shenzhen")
  a := []string{"Chengdu", "Chongqing"}
  citySlice = append(citySlice, a...)
  fmt.Println(citySlice)
}
// [Beijing, Shanghai, Guangzhou, Shenzhen, Chengdu, Chongqing]

Tile Expansion Strategy

By looking at the $GOROOT/src/runtime/slice.go source, the code for expanding is as follows

newcap := old.cap
doublecap := newcap + newcap
if cap > doublecap {
    newcap = cap
} else {
    if old.len < 1024 {
        newcap = doublecap
    } else {
        // Check 0 < newcap to detect overflow
        // and prevent an infinite loop.
        for 0 < newcap && newcap < cap {
            newcap += newcap / 4
        }
        // Set newcap to the requested cap when
        // the newcap calculation overflowed.
        if newcap <= 0 {
            newcap = cap
        }
    }
}
  • First, if the new application capacity (cap) is more than twice the old.cap, the final capacity (newcap) is the new application capacity (cap).
  • Otherwise, if the length of the old slice is less than 1024, the final capacity (newcap) is twice that of the old.cap (newcap=doublecap).
  • Otherwise, if the old slice length is greater than or equal to 1024, the final capacity (newcap) will increase by one-fourth from the old capacity (old.cap), i.e. (newcap=old.cap,for {newcap += newcap/4}) until the final capacity (newcap) is greater than or equal to the new requested capacity (cap), i.e. (newcap >= cap)
  • If the cap calculation value overflows, the cap is the new application capacity (cap)

Copying slices using the copy() function

First: remember that slices are reference types

The copy() function built into the Go language quickly copies data from one slice into another slice space

func main() {
  // copy() copy slice
  a := []int{1, 2, 3, 4, 5}
  b := make([]int, 5, 5)
  copy(b, a)   // Copy elements from slice a to slice b
  fmt.Println(a)
  fmt.Println(b)
  b[0] = 100
  fmt.Println(a)
  fmt.Println(b)
}
// [1 2 3 4 5]
// [1 2 3 4 5]
// [1 2 3 4 5]
// [100 2 3 4 5]

go Delete Slice Element

There is no specific way to delete elements in go, only by index

func main() {
  a := []int{0,1, 2, 3, 4, 5, 6, 7, 8, 9}
  // Delete elements with indexes of 2 to 4
  a = append(a[:2], a[5:]...)
  fmt.Println(a)
}
// [0 1 5 6 7 8 9]

To remove an element indexed as index from slice a, do a = append(a[:index], a[index+1:]...)

func main() {
    a := make([]int, 5, 10)
    fmt.Println(a)
    for i := 0; i < 10; i++ {
        a = append(a, i)
        fmt.Println(a)
    }
}
// [0 0 0 0 0]
// [0 0 0 0 0 0]
// [0 0 0 0 0 0 1]
// [0 0 0 0 0 0 1 2]
// [0 0 0 0 0 0 1 2 3]
// [0 0 0 0 0 0 1 2 3 4]
// [0 0 0 0 0 0 1 2 3 4 5]
// [0 0 0 0 0 0 1 2 3 4 5 6]
// [0 0 0 0 0 0 1 2 3 4 5 6 7]
// [0 0 0 0 0 0 1 2 3 4 5 6 7 8]
// [0 0 0 0 0 0 1 2 3 4 5 6 7 8 9]

Posted by le007 on Fri, 27 Dec 2019 21:41:16 -0800