Slice go-

Primer

Because the length of the array is fixed and a portion of the length of the array of a type, so the array has many limitations. E.g:
1 func arraySum(x [3]int) int{
2     sum := 0
3     for _, v := range x{
4         sum = sum + v
5     }
6     return sum
7 }

The sum function can only accept [3] int type, the other is not supported. Another example:

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

Array a has three elements, we can not continue to add new elements to the array a.

slice

 

Slice (the Slice) is a sequence of variable length have the same type of element. It is based on array type package made of a layer. It is very flexible and supports automatic expansion.
Slice is a reference type, its internal structure contains the address, length, and capacity. Sections generally for rapidly operating a data set.

Defined sections

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

var name []T

among them

  1. name: Specifies the variable name
  2. T: represents the type of element sections

for example:

. 1  FUNC main () {
 2      // declare slice type 
. 3      var A [] String               // declare a string sections 
. 4      var B = [] int {}              // declare an integer slice and initialized 
. 5      var C = [] BOOL { to false , to true } // declare a Boolean slice and initialized 
. 6      var D = [] BOOL { to false , to true } // declare a Boolean slice and initialized 
. 7      fmt.Println (a)               // [] 
. 8      fmt.Println (B )              // [] 
. 9      fmt.Println (C)               // [to false to true] 
10      fmt.Println (A == nil)        // to true 
. 11      fmt.Println (B == nil)        // to false 
12 is      fmt.Println (C = nil =)        // to false
 13 is      // fmt.Println (C == D)    // slice is a reference type, does not support direct comparison, comparison only and nil 
14 }

Slice length and capacity

Slice has its own length and capacity, we can find the length by using the built-in len () function, using the built-cap () function evaluation capacity slice.

Based on the array definition

Slice As the underlying slice is an array, so we can define array-based slice.

. 1  FUNC main () {
 2      // array slice is defined based 
. 3      a: = [ . 5 ] int { 55 , 56 is , 57 is , 58 , 59 }
 . 4      B: = a [ . 1 : . 4 ]                      // array based create a slice, comprising an element A [. 1], A [2], A [. 3] 
. 5      fmt.Println (B)                   // [56 is 57 is 58] 
. 6      fmt.Printf ( " type of B:% T \ n- " , B) // of B type: [] int 
. 7 }

Also supports the following way:

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

Then slice sliced

In addition to an array of get-based slice, we can also get a slice by slice.

1  FUNC main () {
 2      // slices and then slice 
3      A: = [...] String { " Beijing " , " Shanghai " , " Guangzhou " , " Shenzhen " , " Chengdu " , " Chongqing " }
 4      fmt. printf ( " A:% V type:% T len:% D CAP:% D \ n- " , A, A, len (A), CAP (A))
 . 5      B: = A [ . 1 : . 3 ]
 . 6      FMT. printf ( " b:% v of the type:% T len:% Of course:% d \ n", b, b, len(b), cap(b))
7     c := b[1:5]
8     fmt.Printf("c:%v type:%T len:%d  cap:%d\n", c, c, len(c), cap(c))
9 }

Output:

1 A: [Beijing, Shanghai, Guangzhou, Shenzhen, Chengdu, Chongqing] of the type: [ 6 ] String len: 6   CAP: 6 
2 b: [Shanghai Guangzhou] of the type: [] String len: 2   CAP: 5 
3 c: [Guangzhou, Shenzhen, Chengdu, Chongqing] type: [] String len: . 4   CAP: . 4

Note: The sections were then sliced ​​index can not exceed the length of the original array, otherwise there will be error index out of bounds. 

Using the make () function constructs a slice

We are based on the above slice array to create, if a slice need to create dynamic, we need to use the built-in make () function in the following format:

1 make([]T, size, cap)

among them:

  1. T: Slice element types
  2. The number of elements in the slice: size
  3. cap: Slice of capacity

for example:

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

Above a code already allocated internal storage space 10, but in fact only two. Capacity does not affect the current number of elements, so len (a) return 2, cap (a) the capacity of the slice is returned.

Slice of nature

Nature of the package is to slice the bottom of the array, which contains three information: pointers underlying array, the slice length (len) and slice capacity (cap).
For example, there are an array of a: = [8] int { 0, 1, 2, 3, 4, 5, 6, 7}, slice s1: = a [: 5] , corresponding diagram below.

Slice s2: = a [3: 6], the corresponding diagram is as follows: 

 

Sections are not directly comparable

切片之间是不能比较的,我们不能使用==操作符来判断两个切片是否含有全部相等元素。 切片唯一合法的比较操作是和nil比较。 一个nil值的切片并没有底层数组,一个nil值的切片的长度和容量都是0。但是我们不能说一个长度和容量都是0的切片一定是nil,例如下面的示例:

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

所以要判断一个切片是否是空的,要是用len(s) == 0来判断,不应该使用s == nil来判断。

切片的赋值拷贝

下面的代码中演示了拷贝前后两个变量共享底层数组,对一个切片的修改会影响另一个切片的内容,这点需要特别注意。

1 func main() {
2     s1 := make([]int, 3) //[0 0 0]
3     s2 := s1             //将s1直接赋值给s2,s1和s2共用一个底层数组
4     s2[0] = 100
5     fmt.Println(s1) //[100 0 0]
6     fmt.Println(s2) //[100 0 0]
7 }

切片遍历

切片的遍历方式和数组是一致的,支持索引遍历和for range遍历。

 

 1 func main() {
 2     s := []int{1, 3, 5}
 3 
 4     for i := 0; i < len(s); i++ {
 5         fmt.Println(i, s[i])
 6     }
 7 
 8     for index, value := range s {
 9         fmt.Println(index, value)
10     }
11 }

append()方法为切片添加元素

Go语言的内建函数append()可以为切片动态添加元素,每个切片会指向一个底层数组,这个数组的容量够用就添加新增元素。当底层数组不能容纳新增的元素时,切片就会自动按照一定的策略进行“扩容”,此时该切片指向的底层数组就会更换。“扩容”操作往往发生在append()函数调用时,所以我们通常都需要用原变量接收append函数的返回值。

举个例子:

1 func main() {
2     //append()添加元素和切片扩容
3     var numSlice []int
4     for i := 0; i < 10; i++ {
5         numSlice = append(numSlice, i)
6         fmt.Printf("%v  len:%d  cap:%d  ptr:%p\n", numSlice, len(numSlice), cap(numSlice), numSlice)
7     }
8 }

输出:

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

从上面的结果可以看出:

  1. append()函数将元素追加到切片的最后并返回该切片。
  2. 切片numSlice的容量按照1,2,4,8,16这样的规则自动进行扩容,每次扩容后都是扩容前的2倍。

append()函数还支持一次性追加多个元素。 例如:

1 var citySlice []string
2 // 追加一个元素
3 citySlice = append(citySlice, "北京")
4 // 追加多个元素
5 citySlice = append(citySlice, "上海", "广州", "深圳")
6 // 追加切片
7 a := []string{"成都", "重庆"}
8 citySlice = append(citySlice, a...)
9 fmt.Println(citySlice) //[北京 上海 广州 深圳 成都 重庆]

切片的扩容策略

可以通过查看$GOROOT/src/runtime/slice.go源码,其中扩容相关代码如下:

 1 newcap := old.cap
 2 doublecap := newcap + newcap
 3 if cap > doublecap {
 4     newcap = cap
 5 } else {
 6     if old.len < 1024 {
 7         newcap = doublecap
 8     } else {
 9         // Check 0 < newcap to detect overflow
10         // and prevent an infinite loop.
11         for 0 < newcap && newcap < cap {
12             newcap += newcap / 4
13         }
14         // Set newcap to the requested cap when
15         // the newcap calculation overflowed.
16         if newcap <= 0 {
17             newcap = cap
18         }
19     }
20 }

从上面的代码可以看出以下内容:

  • 首先判断,如果新申请容量(cap)大于2倍的旧容量(old.cap),最终容量(newcap)就是新申请的容量(cap)。
  • 否则判断,如果旧切片的长度小于1024,则最终容量(newcap)就是旧容量(old.cap)的两倍,即(newcap=doublecap),
  • 否则判断,如果旧切片长度大于等于1024,则最终容量(newcap)从旧容量(old.cap)开始循环增加原来的1/4,即(newcap=old.cap,for {newcap += newcap/4})直到最终容量(newcap)大于等于新申请的容量(cap),即(newcap >= cap)
  • 如果最终容量(cap)计算值溢出,则最终容量(cap)就是新申请容量(cap)。
  • 需要注意的是,切片扩容还会根据切片中元素的类型不同而做不同的处理,比如int和string类型的处理方式就不一样。

使用copy()函数复制切片

首先我们来看一个问题:

1 func main() {
2     a := []int{1, 2, 3, 4, 5}
3     b := a
4     fmt.Println(a) //[1 2 3 4 5]
5     fmt.Println(b) //[1 2 3 4 5]
6     b[0] = 1000
7     fmt.Println(a) //[1000 2 3 4 5]
8     fmt.Println(b) //[1000 2 3 4 5]
9 }

由于切片是引用类型,所以a和b其实都指向了同一块内存地址。修改b的同时a的值也会发生变化。
Go语言内建的copy()函数可以迅速地将一个切片的数据复制到另外一个切片空间中,copy()函数的使用格式如下:

1 copy(destSlice, srcSlice []T)

其中:
srcSlice: 数据来源切片
destSlice: 目标切片
举个例子:

 1 func main() {
 2     // copy()复制切片
 3     a := []int{1, 2, 3, 4, 5}
 4     c := make([]int, 5, 5)
 5     copy(c, a)     //使用copy()函数将切片a中的元素复制到切片c
 6     fmt.Println(a) //[1 2 3 4 5]
 7     fmt.Println(c) //[1 2 3 4 5]
 8     c[0] = 1000
 9     fmt.Println(a) //[1 2 3 4 5]
10     fmt.Println(c) //[1000 2 3 4 5]
11 }

从切片中删除元素

Go语言中并没有删除切片元素的专用方法,我们可以使用切片本身的特性来删除元素。 代码如下:

1 func main() {
2     // 从切片中删除元素
3     a := []int{30, 31, 32, 33, 34, 35, 36, 37}
4     // 要删除索引为2的元素
5     a = append(a[:2], a[3:]...)
6     fmt.Println(a) //[30 31 33 34 35 36 37]
7 }

总结一下就是:要从切片a中删除索引为index的元素,操作方法是a = append(a[:index], a[index+1:]...)

append用法:

 1 func testArray() {
 2     var a = make([]string, 5, 10)
 3     fmt.Println(a)
 4     fmt.Println(len(a))
 5     fmt.Println(cap(a))
 6     for i := 0; i < 10; i++ {
 7         a = append(a, fmt.Sprintf("%v", i))
 8     }
 9     fmt.Println(a)
10     fmt.Println(len(a))
11     fmt.Println(cap(a))
12 }

输出:

1 [    ]
2 5
3 10
4 [     0 1 2 3 4 5 6 7 8 9]
5 15
6 20

 

 

 

 

 

 

 

Guess you like

Origin www.cnblogs.com/DI-DIAO/p/12393149.html