Slice of go language foundation

Time:2021-5-11

This paper mainly introduces slice in go language and its basic use.

Because the length of the array is fixed and the length of the array is a part of the type, the array has many limitations. For example:

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

This summation function can only accept[3]intType, none of the others are supported. For example,

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

There are already three elements in array A. We can’t add new elements to array a any more.

section

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

A slice is a reference type whose internal structure containsaddresslengthandcapacity. Slicing is usually used to quickly manipulate a data set.

Definition of slice

The basic syntax for declaring a slice type is as follows:

var name []T

Among them,

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

for instance:

func main() {
	//Declare slice type
	Var a [] string // declares a string slice
	Var B = [] int {} // declare an integer slice and initialize it
	Var C = [] bool {false, true} // declare a Boolean slice and initialize it
	Var d = [] bool {false, true} // declare a Boolean slice and initialize it
	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. Direct comparison is not supported. It can only be compared with nil
}

Length and capacity of slices

Slice has its own length and capacity. We can calculate the length by using the built-in len() function and the capacity by using the built-in cap() function.

Define slice based on array

Since the bottom layer of slicing is an array, we can define Slicing Based on the array.

func main() {
	//Define slice based on array
	a := [5]int{55, 56, 57, 58, 59}
	B: = a [1:4] // create slices based on array a, including elements a [1], a [2], a [3]
	fmt.Println(b)                  //[56 57 58]
	fmt.Printf("type of b:%T\n", b) //type of b:[]int
}

The following methods are also supported:

c := a[1:] //[56 57 58 59]
d := a[:4] //[55 56 57 58]
e := a[:]  //[55 56 57 58 59]

Slice and slice again

In addition to getting slices based on arrays, we can also get slices by slicing.

func main() {
	//Slice and slice again
	A: = [...] string {"Beijing", "Shanghai", "Guangzhou", "Shenzhen", "Chengdu", "Chongqing"}
	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))
}

Output:

a: [Beijing Shanghai Guangzhou Shenzhen Chengdu Chongqing] type: [6] string len:6  cap :6
b: [Shanghai, Guangzhou] type: [] string len:2  cap :5
c: [Guangzhou, Shenzhen, Chengdu, Chongqing] type: [] string len:4  cap :4

be careful:When slicing a slice again, the index should not exceed the length of the original array, otherwise the index will go beyond the bounds.

Using the make() function to construct slices

We all create slices based on arrays. If we need to create a slice dynamically, we need to use the built-inmake()Function, the format is as follows:

make([]T, size, cap)

Among them:

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

for instance:

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

In the code abovea10 internal storage spaces have been allocated, but only 2 are actually used. Capacity does not affect the current number of elements, solen(a)Return to 2,cap(a)Returns the capacity of the slice.

The essence of slicing

The essence of slicing is to encapsulate the underlying array, which contains three information: the pointer of the underlying array, the length of slicing (len) and the capacity of slicing (CAP).

For example, there is now an arraya := [8]int{0, 1, 2, 3, 4, 5, 6, 7}, slices1 := a[:5]The corresponding schematic diagram is as follows.  sections2 := a[3:6]The corresponding schematic diagram is as follows:  

Slices cannot be compared directly

We can’t compare slices, we can’t use them==Operator to determine whether two slices contain all equal elements. The only legal comparison operation of slicing is andnilComparison. OnenilThe slice of the value has no underlying array, anilThe length and capacity of the slice of the value are both 0. But we can’t say that a slice whose length and capacity are both 0 must be 0nil, such as 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!=nil

So to determine whether a slice is empty, if you uselen(s) == 0To judge, it should not be useds == nilTo judge.

Assignment copy of slice

The following code demonstrates that the two variables before and after copying share the underlying array, and the modification of one slice will affect the content of the other slice, which needs special attention.

func main() {
	s1 := make([]int, 3) //[0 0 0]
	S2: = S1 // assign S1 to S2 directly. S1 and S2 share the same underlying array
	s2[0] = 100
	fmt.Println(s1) //[100 0 0]
	fmt.Println(s2) //[100 0 0]
}

Slice traversal

Slice traversal and array is consistent, support index traversal and array traversalfor rangeTraversal.

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

Built in function of go languageappend()You can dynamically add elements to slices. Each slice will point to an underlying array. If the capacity of the array is enough, you can add new elements. When the underlying array cannot hold the new elements, the slice will automatically “expand” according to a certain strategy, and the underlying array pointed to by the slice will be replaced“ “Capacity expansion” operations often occur inappend()Function call, so we usually need to use the original variable to receive the return value of the append function.

for instance:

func main() {
	//Append() to 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)
	}
}

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:0xc0000b8000

It can be seen from the above results that:

  1. append()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 rules of 1, 2, 4, 8 and 16. After each expansion, the capacity is twice that before.

The append() function also supports appending multiple elements at one time. For example:

var citySlice []string
//Append an element
Cityslice = append (cityslice, Beijing)
//Appending multiple elements
Cityslice = append (cityslice, Shanghai, Guangzhou, Shenzhen)
//Additional slice
A: = [] string {"Chengdu", "Chongqing"}
citySlice = append(citySlice, a...)
FMT. Println (cityslice) // [Beijing, Shanghai, Guangzhou, Shenzhen, Chengdu, Chongqing]

Expansion strategy of slice

You can view the$GOROOT/src/runtime/slice.goSource code, including expansion related code 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
		}
	}
}

From the above code, we can see the following:

  • First of all, if the new applied capacity (CAP) is more 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 the old capacity (old. Cap), that is, (newcap = doublecap),
  • Otherwise, if the length of the old slice is greater than or equal to 1024, the final capacity (newcap) will be increased by 1 / 4 from the old capacity (old. Cap), that is, (newcap = old. Cap, for {newcap + = newcap / 4}) until the final capacity (newcap) is greater than or equal to the newly applied capacity (CAP), that is, (newcap > = cap)
  • If the calculated value of the final capacity (CAP) overflows, the final capacity (CAP) is the newly applied capacity (CAP).

It should be noted that slice expansion will also do different processing according to the types of elements in the slice, such asintandstringTypes are handled differently.

Use the copy() function to copy slices

First of all, let’s look at a problem

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]
}

Since the slice is a reference type, a and B actually point to the same memory address. When B is modified, the value of a will also change.

Go language built incopy()Function can quickly copy the data of one slice to another slice space,copy()The format of the function is as follows:

copy(destSlice, srcSlice []T)

Among them:

  • Srcslice: data source slice
  • Destslice: target slice

for instance:

func main() {
	//Copy () copies the slice
	a := []int{1, 2, 3, 4, 5}
	c := make([]int, 5, 5)
	Copy (C, a) // use the copy() function to copy the elements in slice a to slice C
	fmt.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]
	fmt.Println(c) //[1000 2 3 4 5]
}

Remove element from slice

There is no special method to delete sliced elements in go language. We can use the characteristics of slicing itself to delete elements. The code is as follows:

func main() {
	//Remove element from slice
	a := []int{30, 31, 32, 33, 34, 35, 36, 37}
	//To delete an element with index 2
	a = append(a[:2], a[3:]...)
	fmt.Println(a) //[30 31 33 34 35 36 37]
}

To sum up, delete the index from slice aindexThe operation method isa = append(a[:index], a[index+1:]...)

Exercises

1. Please write the output of the following code.

func main() {
	var a = make([]string, 5, 10)
	for i := 0; i < 10; i++ {
		a = append(a, fmt.Sprintf("%v", i))
	}
	fmt.Println(a)
}

2. Please use the built-insortPackage pair arrayvar a = [...]int{3, 7, 8, 9, 1}To sort (additional questions, self check data to answer).

Recommended Today

Looking for frustration 1.0

I believe you have a basic understanding of trust in yesterday’s article. Today we will give a complete introduction to trust. Why choose rust It’s a language that gives everyone the ability to build reliable and efficient software. You can’t write unsafe code here (unsafe block is not in the scope of discussion). Most of […]