Arrays have a fixed length and are part of a type, so they have a lot of limitations. Such as:

func arraySum(x [3]int) int{
    sum := 0
    for _, v := range x{
        sum = sum + v
    }
    return sum
}
Copy the code

This summation function accepts only [3] ints, and nothing else. For example,

a := [3]int{1, 2, 3}
Copy the code

We already have three elements in array A, so we can’t add any more elements to array A.

slice

A Slice is a variable-length sequence of elements of the same type. It is a layer of encapsulation based on the array type. It is very flexible and supports automatic expansion.

A slice is a reference type whose internal structure contains an address, length, and capacity. Slicing is generally used to quickly manipulate a collection of data.

Definition of slice

The basic syntax for declaring slice types is as follows:

var name []T
Copy the code

Among them,

  • Name: indicates the variable name
  • T: indicates the element type in the slice

Here’s an example:

Var b = []int{} // Declare an integer slice and initialize var c = []bool{false, Var d = []bool{false, // Declare a Boolean slice and initialize fmt.println (a) //[] fmt.println (b) //[] fmt.println (c) //[false true] fmt.println (a == nil) //true Fmt.println (b == nil) //false fmt.println (c == nil) //false // fmt.println (c == d) // Slice is a reference type, can not support direct comparison, can only compare with nil}Copy the code

Length and capacity of slice

Slices have their own length and capacity. We can calculate the length of slices by using the built-in len() function and the capacity of slices by using the built-in cap() function.

Slice expression

A slice expression constructs a substring or slice from a string, array, or pointer to an array or slice. It comes in two variants: a simple form that specifies low and high index bounds, and a full form that specifies capacity in addition to low and high index bounds.

Simple slice expression

The bottom layer of a slice is an array, so we can get a slice based on an array through a slice expression. Low and high in the slice expression represent an index range (left included, right excluded), that is, in the following code, elements 1<= index value <4 from array A are selected to form slice S, the length of the slice obtained is =high-low, and the capacity is equal to the capacity of the underlying array of the slice obtained.

func main() {
    a := [5]int{1, 2, 3, 4, 5}
    s := a[1:3]  // s := a[low:high]
    fmt.Printf("s:%v len(s):%v cap(s):%v\n", s, len(s), cap(s))
}
Copy the code

Output:

s:[2 3] len(s):2 cap(s):4
Copy the code

For convenience, you can omit any indexes in the slice expression. Omitting low defaults to 0; Omitting high defaults to the length of the slice operand:

A [2] / / is equivalent to a [2: len (a)] [3] / / a is equal to a [3-0] a [:] / / is equivalent to a [0: len (a)]Copy the code

Note: For arrays or strings, the index is valid if 0 <= low <= high <= len(a), otherwise the index is out of range.

When the slice expression is executed for the slice again (slice again slice), the upper bound of high is the capacity cap(a) of the slice, not the length. A constant index must be non-negative and can be represented by a value of type int; For arrays or constant strings, the constant index must also be in a valid range. If both low and high are constants, they must satisfy low <= high. A runtime panic occurs if the index is out of range at run time.

func main() { a := [5]int{1, 2, 3, 4, 5} s := a[1:3] // s := a[low:high] fmt.Printf("s:%v len(s):%v cap(s):%v\n", s, Len (s), cap (s)) s2: = s [did] / / index of cap cap (s) is not len (s) FMT. Printf (" s2: % v len (s2) : % v cap (s2) : % v \ n ", s2, len (s2), cap(s2)) }Copy the code

Output:

s:[2 3] len(s):2 cap(s):4
s2:[5] len(s2):1 cap(s2):1
Copy the code

Complete slice expression

For arrays, Pointers to arrays, or slice a(not strings) support full slice expressions:

a[low : high : max]
Copy the code

The above code constructs a slice of the same type, length, and elements as the simple slice expression A [low: high]. In addition, it sets the capacity of the resulting slice to max-low. Only the first index value (low) can be omitted in the full slice expression; It defaults to 0.

func main() {
    a := [5]int{1, 2, 3, 4, 5}
    t := a[1:3:5]
    fmt.Printf("t:%v len(t):%v cap(t):%v\n", t, len(t), cap(t))
}
Copy the code

Output result:

t:[2 3] len(t):2 cap(t):4
Copy the code

The conditions for a complete slice expression are 0 <= low <= high <= Max <= cap(a), and other conditions are the same as those for a simple slice expression.

Use the make() function to construct slices

If you want to create a slice dynamically, you need to use the built-in make() function, which has the following format:

make([]T, size, cap)
Copy the code

Among them:

  • T: the element type of the slice
  • Size: the number of elements in the slice
  • Cap: indicates the slice capacity

Here’s an example:

func main() {
    a := make([]int, 2, 10)
    fmt.Println(a)      //[0 0]
    fmt.Println(len(a)) //2
    fmt.Println(cap(a)) //10
}
Copy the code

In the code above, 10 internal storage Spaces of A have been allocated, but only 2 are actually used. Capacity does not affect the number of current elements, so len(a) returns 2 and cap(a) returns the capacity of the slice.

The nature of slices

The essence of slicing is to encapsulate the underlying array. It contains three pieces of information: the pointer to the underlying array, the length of the slice (Len) and the capacity of the slice (CAP).

For example, we now have an array a := [8]int{0, 1, 2, 3, 4, 5, 6, 7}, sliced s1 := a[:5], as shown in the following diagram.

Section S2 := a[3:6], corresponding schematic diagram is as follows:

Determine whether the slice is empty

To check if the slice is empty, always use len(s) == 0 and should not use s == nil.

Slices cannot be directly compared

Slices cannot be compared, and we cannot use the == operator to determine whether two slices contain all equal elements. The only valid comparison operation for slicing is to compare to nil. A nil slice has no underlying array. A nil slice has a length and a capacity of 0. But we can’t say that a slice with both length and capacity 0 is nil, as in the following example:

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! =nilCopy the code

Therefore, to determine whether a slice is empty, len(s) == 0 should not be used to determine s == nil.

An assigned copy of a slice

The following code demonstrates that both variables share the underlying array before and after the copy. It is important to note that changes to one slice affect the contents of the other.

Func main() {s1 := make([]int, 3) //[0 0 0] s2 := s1 Println(s1) //[100 00] Println(s2) //[100 00]}Copy the code

Slice traversal

Slice traversal is the same as array traversal, supporting index traversal and for range traversal.

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)
    }
}
Copy the code

The append() method adds elements to the slice

The Go language’s built-in function Append () can dynamically add elements to slices. You can add one element at a time, you can add multiple elements, or you can add elements from another slice (followed by…). .

func main(){ var s []int s = append(s, 1) // [1] s = append(s, 2, 3, 4) // [1 2 3 4] s2 := []int{5, 6, 7} s = append(s, s2...) // [1 2 3 4 5 6 7]}Copy the code

Note: A zero-value slice declared by var can be used directly in the append() function without initialization.

var s []int
s = append(s, 1, 2, 3)
Copy the code

There is no need to initialize a slice and pass it into the append() function as shown below,

Var s = make([]int) // appEnd (s, 1, 2, 3)Copy the code

Each slice points to an underlying array, and when the array is large enough, new elements are added. When the underlying array cannot contain new elements, the slice will be automatically expanded according to certain policies, and the underlying array pointed to by the slice will be replaced. The “expansion” operation usually occurs when the append() function is called, so we usually need to receive the return value of the Append function in the original variable.

Here’s an example:

Func main() {//append() 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) } }Copy the code

Output:

[0] len:1 cap:1 ptr:0xc0000a8000 [0 1] len:2 cap:2 ptr:0xc0000a8040 [0 1 2] len:3 cap:4 ptr:0xc0000b2020 [0 1 2 3] len:4  cap:4 ptr:0xc0000b2020 [0 1 2 3 4] len:5 cap:8 ptr:0xc0000b6000 [0 1 2 3 4 5] len:6 cap:8 ptr:0xc0000b6000 [0 1 2 3 4 5  6] len:7 cap:8 ptr:0xc0000b6000 [0 1 2 3 4 5 6 7] len:8 cap:8 ptr:0xc0000b6000 [0 1 2 3 4 5 6 7 8] len:9 cap:16 ptr:0xc0000b8000 [0 1 2 3 4 5 6 7 8 9] len:10 cap:16 ptr:0xc0000b8000Copy the code

As can be seen from the above results:

  1. append()The function appends the element to the end of the slice and returns the slice.
  2. The capacity of numSlice is automatically expanded according to the rule 1,2,4,8,16. After each expansion, the capacity is doubled.

The append() function also supports appending multiple elements at once. Such as:

Var citySlice []string citySlice = AppEnd (citySlice, "Beijing ") var citySlice = Append (citySlice," Shanghai ") "Guangzhou", "shenzhen") / / additional section a: = [] string {" chengdu ", "chongqing"} citySlice = append (citySlice, a...). Println(citySlice) //[Beijing Shanghai Guangzhou Shenzhen Chengdu Chongqing]Copy the code

Slice capacity expansion policy

Can see $GOROOT/SRC/runtime/slice. Go source, expansion and the relevant code 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
            }
    }
}
Copy the code

From the code above you can see the following:

  • First, if the new applied capacity (CAP) is greater than twice the old capacity (old.cap), the final capacity (NewCap) is the new applied capacity (CAP).
  • Otherwise, if the length of the old slice is less than 1024, the final capacity (newcap) is twice that of the old capacity (old.cap), i.e. (newCap = Doublecap),
  • Otherwise, if the length of the old slice is greater than or equal to 1024, then the final capacity (newcap) increases by 1/4 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 capacity (CAP), i.e. (newCap >= CAP)
  • If the calculated final capacity (CAP) overflows, the final capacity (CAP) is the new applied capacity (CAP).

It should be noted that slice expansion also performs different processing according to the type of elements in the slice. For example, the processing method of int and string types is different.

Use the copy() function to copy slices

Let’s start with a question:

func main() {
    a := []int{1, 2, 3, 4, 5}
    b := a
    fmt.Println(a) //[1 2 3 4 5]
    fmt.Println(b) //[1 2 3 4 5]
    b[0] = 1000
    fmt.Println(a) //[1000 2 3 4 5]
    fmt.Println(b) //[1000 2 3 4 5]
}
Copy the code

Since slices are references, both A and B actually refer to the same memory address. When you change B, you change the value of A.

Go’s built-in copy() function can quickly copy data from one slice to another. The format of copy() function is as follows:

copy(destSlice, srcSlice []T)
Copy the code

Among them:

  • SrcSlice: indicates the slice of data source
  • DestSlice: indicates the destination slice

Here’s an example:

Func main () {/ / copy (copy) section a: = int [] {1, 2, 3, 4, 5} c: = make (int [], 5, 5) copy (c, Println(a) //[1 2 3 4 5] fmt.println (c) //[1 2 3 4 5] c[0] = 1000 fmt.println (a) //[1 2 3 4 5] c[0] = 1000 fmt.println (a) //[1 2 3 4 5] fmt.Println(c) //[1000 2 3 4 5] }Copy the code

Deletes elements from the slice

There is no special way to delete slice elements in Go, we can use the features of slice itself to delete elements. The code is as follows:

Func main() {// delete element a := []int{30, 31, 32, 33, 34, 35, 36, 37} // delete element A = append(a[:2], a[3:]... fmt.Println(a) //[30 31 33 34 35 36 37] }Copy the code

A = append(a[:index], a[index+1:]…)