preface

A slice is a composite data type, similar to an array in that it holds elements of the same data type, but the size of the array is fixed, whereas a slice is variable in size and can be automatically resized on demand. Slicing is implemented based on the underlying array, which is an abstraction of the array. A slice is a small data structure with only three fields: a pointer to the underlying array, the number of elements that can be accessed (the slice length), and the number of elements allowed to grow (the slice capacity).

Declaration and initialization

Make () to create

Create an empty slice using built-in functions like:

s := make([]type.len.cap)  // Len length, cap capacity
Copy the code

You can also specify len only, so the size and length of the slice are the same. The Go language provides built-in functions len and Cap to return the length and capacity of the slice, respectively.

// Declare an integer slice of length 3 and capacity 5
s1 := make([]int.3.5)
fmt.Println(len(s1),cap(s1))   // Output: 3 5

// Declare a string slice of length and capacity 5
s2 := make([]string.5)
fmt.Println(len(s2),cap(s2))   // Output: 5 5
Copy the code

When the slice is created, the default value is zero for the elements of the array if no literals are specified. The capacity of a slice is the size of the array at the bottom of the slice. We can only access the elements within the length of the slice. As shown in the figure in section 1, we can only access the third element after storing the structure of the integer slice with the length of 3 and the remaining 2 elements can only be accessed after the slice is expanded. So, it’s obvious: capacity >= length, we can’t create slices that are longer than capacity.

s1 := make([]int.5.3)
Len larger than cap in make([]int)
Copy the code

Create slices using literals

Created with a literal, you specify the value to initialize

s := []int{1.2.3.4.5}     // An integer slice of length and capacity 5
Copy the code

This is similar to creating an array, but without specifying the value [], the size and length of the slice are the same and will be derived from the specified literal. The difference between:

// Create an array of size 10
s := [10]int{1.2.3.4.5}
// Create a slice
s := []int{1.2.3.4.5}
Copy the code

We can also initialize the value of only one index:

s := []int{4:1}
fmt.Println(len(s),cap(s))  // Output: 5 5
fmt.Println(s)		// Output: [0 0 0 0 1]
Copy the code

The fifth element is specified as 1 and the other elements are initialized to 0.

Create a slice based on an existing array or slice

The operator [start:end], abbreviated as [I :j], is used to intercept any part of the existing array or slice from index I to the end of index J and return a new slice. The value of the new slice contains the value of the I index of the original slice, but does not contain the value of the J index. Both I and j are optional. If I is omitted, the default value is 0. If j is omitted, the default value is the length of the original slice or array. Neither I nor j can exceed this length.

s := []int{0.1.2.3.4.5.6.7.8.9}

fmt.Println("s[:]", s[:])
fmt.Println("s[2:]", s[2:])
fmt.Println("s[:4]", s[:4])
fmt.Println("s[2:4]", s[2:4])
Copy the code

Output You may be wondering: how do you calculate the length and capacity of a new slice obtained by intercepting it? Of course, we can use the built-in functions len and cap to get it directly. If we know how to calculate it, we can deal with the problem more easily. The length and capacity of the new slice obtained after performing the [I,j] operation on the slice with the underlying array size of K are: length: j-i Capacity: k-i Take s[2:4] as an example, the size of the underlying array of the original slice is 10, so the length of the new slice is 4-2=2, and the capacity is 10-2=8. You can use the built-in function to verify:

s1 := s[2:4]
fmt.Println(len(s1),cap(s1)) // Output: 2 8
Copy the code

Slice [I :j:k] is used to limit the size of a new slice.

s2 := s[2:4:8]
fmt.Println(s2)  // Output: [2 3]
Copy the code

How to calculate the length and capacity: length J-i, capacity K-i. Therefore, the length and capacity of section S2 are 2 and 6 respectively. Panic: Runtime error: Slice bounds out of range For example, in the example above, the third index cannot exceed 10. Let’s look at an example:

s := []int{0.1.2.3.4.5}

fmt.Println("before,s:",s)
s1 := s[1:4]
fmt.Println("before,s1:",s1)
s1[1] = 10
fmt.Println("after,s1:",s1)
fmt.Println("after,s:",s)
Copy the code

Output:

before,s: [0 1 2 3 4 5]
before,s1: [1 2 3]
after,s1: [1 10 3]
after,s: [0 1 10 3 4 5]
Copy the code

This example shows that the original slice and the new slice are based on the same underlying array, so when you modify the underlying array, the value of the original slice will be changed. The same is true for array-based slicing.

s
s1
1
s1
1

Using slice

Slicing is used in a similar way to using arrays. You can retrieve and modify the value of an element directly through the index.

s := []int{1.2.3.4.5}
fmt.Println(s[1])   // Get the value output: 2
s[1] = 10	  	/ / modify the value
fmt.Println(s)   // Output: [1 10 3 4 5]
Copy the code

Only elements within the length of the slice can be accessed, otherwise an error is reported

s := []int{1.2.3.4.5}
s1 := s[2:3]			
fmt.Println(s1[1]) 
Copy the code

In the example above, the size of s1 is 3 and the length is 1, so the first element of s1[0] can only be accessed

Elements associated with the size of a slice can only be used to grow the slice, and must be merged into the length of the slice before they can be used.

The advantage of slicing over arrays is that it can grow on demand, similar to dynamic arrays. Go provides a built-in append function that handles the details of slice growth, so we can just use it. Function prototype:

func append(slice []Type, elems ... Type) []Type
Copy the code

Using the append function, you need a slice to be operated on and one (more) append value to return a new slice of the same data type.

s := []int{1.2.3.4.5}
newS := s[2:4]
newS = append(newS, 50)
fmt.Println(s, newS)
fmt.Println(&s[2] == &newS[0])
Copy the code

Output:

[1 2 3 4 50] [3 4 50]
true
Copy the code

In the above example, a new slice newS with length 2 and capacity 3 (available capacity 1) is intercepted, and an element 50 is appended to the slice newS through the append function. Append element 50 before:

newS
s



What happens when the available slice capacity does not hold the elements that need to be appended?

s := []int{1.2.3.4.5.6.7.8}
s1 := s[2:4]
fmt.Printf("before -> s=%v\n", s)
fmt.Printf("before -> s1=%v\n", s1)
fmt.Printf("before -> len=%d, cap=%d\n".len(s1), cap(s1))
fmt.Println("&s[2] == &s1[0] is", &s[2] == &s1[0])

s1 = append(s1, 60.70.80.90.100.110)
fmt.Printf("after -> s=%v\n", s)
fmt.Printf("after -> s1=%v\n", s1)
fmt.Printf("after -> len=%d, cap=%d\n".len(s1), cap(s1))
fmt.Println("&s[2] == &s1[0] is", &s[2] == &s1[0])
Copy the code

Output:

before -> s=[1 2 3 4 5 6 7 8]
before -> s1=[3 4]
before -> len=2.cap=6
&s[2] == &s1[0] is true
after -> s=[1 2 3 4 5 6 7 8]
after -> s1=[3 4 60 70 80 90 100 110]
after -> len=8.cap=12
&s[2] == &s1[0] is false
Copy the code

Append elements 60, 70, 80, 90, 100, and 110 before:

append

Generally we are creating new slices, best to let the new section length and capacity, so we are in the additional operation will generate a new underlying array, and the original array separation, won’t cause strange problem because of the common underlying array, because when sharing array, modify the content will affect multiple slices.

The append function intelligently increases the size of the underlying array, doubling the size when the array size is <=1024. Beyond 1024, the growth factor becomes 1.25, or 25% more capacity at a time. Go provides… Operator to allow appending one slice to another:

s := []int{1.2.3.4.5}
s1 := []int{6.7.8}
s = append(s,s1...)
fmt.Println(s,s1)
Copy the code

Output:

[1 2 3 4 5 6 7 8] [6 7 8]
Copy the code

Iterative slice

Use the for loop to iterate over slices in conjunction with len:

s := []int{1.2.3.4.5}
for i:=0; i<len(s) ; i++ { fmt.Printf("Index:%d,Value:%d\n",i,s[i])
}
Copy the code

Iterating slices using for range:

s := []int{1.2.3.4.5}
for i,v := range s {
	fmt.Printf("Index:%d,Value:%d\n",i,v)
}

// Use '_' to ignore the return value
s := []int{1.2.3.4.5}
for _,v := range s {
	fmt.Printf("Value:%d\n",v)
}
Copy the code

Note that range returns a copy of the sliced element, not a reference to the element. Using the address of the value variable as a pointer to each element causes an error.

s := []int{1.2.3.4.5}
for i,v := range s {
	fmt.Printf("v:%d,v_addr:%p,elem_addr:%p\n",v,&v,&s[i])
}
Copy the code

Output:

v:1,v_addr:0xc000018058,elem_addr:0xc000016120
v:2,v_addr:0xc000018058,elem_addr:0xc000016128
v:3,v_addr:0xc000018058,elem_addr:0xc000016130
v:4,v_addr:0xc000018058,elem_addr:0xc000016138
v:5,v_addr:0xc000018058,elem_addr:0xc000016140
Copy the code

As you can see, the address of V is always the same, because the variables returned by the iteration are new variables that are successively assigned according to the slices during the iteration. Well, that’s it for today, and we’ll talk more about Slice in the next section!


Original article, if need to be reproduced, please indicate the source! Check out “Golang is coming” or go to seekload.net for more great articles.

The public account “Golang is coming” has prepared a mystery learning gift package for you, and the background replies [ebook] to get it!