Go切片「slice」的介绍

131 阅读5分钟

在前面的文章中,我们简单的介绍了数组,但没说数组的缺点是什么?数组有个缺点就是初始化之后数组的长度就固定了,不能改变。但有时,我们不能在一开始就很精确的知道,我们要存储多少个元素,这样的话,对初始化数组的时候,就不好去给定个初始长度,给少了不行,给太多了又很浪费。

在Go中,我们可以使用它给我们提供的slice(切片),可以不用关心初始化数组长度这个问题。

什么是切片?

切片中有3个参数

  • len:表示切片当前可存储元素的个数
  • cap:表示切片最大可存储元素的个数
  • ptr:存储的是指向数组的指针(可以看做是存储了数组)

注:len <= cap

切片不是数组,但切片中有个指向数组的指针,是专门用来引用数组的,所以切片是什么?我觉得切片是一个引用了数组的结构体。

在这里插入图片描述

切片的创建

直接创建切片

// len代表了切片的长度,cap代表了切片的容量
make([]类型, len, cap)

// len = cap,只要填一个参数,就同时指代了len和cap
make([]类型, (len = cap))

代码示例

slice := make([]int, 6, 15)
fmt.Println("切片的长度:", len(slice), "切片的容量:", cap(slice))

输出

切片的长度: 6 切片的容量: 15

slice := make([]int, 6)
fmt.Println("切片的长度:", len(slice), "切片的容量:", cap(slice))

输出

切片的长度: 6 切片的容量: 6

通过数组创建切片

注:[左:右),表示数组在转成切片的时候,包左不包右,看下面代码理解这个包左不包右的意思

值1:表示从哪个位置开始切

值2:表示截止切的位置,可以从这个知道len的长度:len = 值2 - 值1

值3:表示切片的容量为:cap = 值3 - 值1

变量名 := 数组[值1:值2:值3]

代码示例

a := [10]int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
slice := a[2:6:8]
fmt.Println("切片的长度为:", len(slice), "\n切片的容量为:", cap(slice), "\n切片的值为:", slice)

输出

切片的长度为: 4 
切片的容量为: 6 
切片的值为: [2 3 4 5]

len = 冒号右边的值 - 冒号左边的值

cap = 数组的长度 - 冒号左边的值

左边不填就是0,右边不填就是数组长度

变量名 := 数组[值1:值2]
a := [10]int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
slice := a[2:7]
fmt.Println("切片的长度为:", len(slice), "\n切片的容量为:", cap(slice), "\n切片的值为:", slice)

输出

切片的长度为: 5 
切片的容量为: 8 
切片的值为: [2 3 4 5 6]

表示把整个数组,转换成切片

len = 数组的长度 - 冒号最左边的值(不填就认为是0)

cap = 数组的长度 - 冒号最左边的值(不填就认为是0)

左边不填就是0,右边不填就是数组长度

slice := a[:]
a := [10]int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
slice := a[:]
fmt.Println("切片的长度为:", len(slice), "\n切片的容量为:", cap(slice), "\n切片的值为:", slice)

输出

切片的长度为: 10 
切片的容量为: 10 
切片的值为: [0 1 2 3 4 5 6 7 8 9]

通过数组创建的切片,改变切片值会影响数组?

a := [10]int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
	slice := a[:]
	fmt.Println("改变前数组的值:", a, "\n改变前切片的值:", slice)
	slice[5] += 100
	slice[7] += 10
	fmt.Println("改变后数组的值:", a, "\n改变后切片的值:", slice)

输出

改变前数组的值: [0 1 2 3 4 5 6 7 8 9] 
改变前切片的值: [0 1 2 3 4 5 6 7 8 9]
改变后数组的值: [0 1 2 3 4 105 6 17 8 9] 
改变后切片的值: [0 1 2 3 4 105 6 17 8 9]

从输出来看,切片的值改变是会影响数组的值也变化的 在这里插入图片描述 从这张图可以看出,切片指向了数组a,所以切片的改变会改变数组a

当什么时候切片的值改变了,数组a的值不变

a := [10]int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
	slice := a[:]
	fmt.Println("改变前数组的值:", a, "\n改变前切片的值:", slice)
	fmt.Println("-------------------------------------")

	slice[5] += 100
	slice[7] += 10
	fmt.Println("改变后数组的值:", a, "\n改变后切片的值:", slice)
	fmt.Println("-------------------------------------")

	// 往切片中添加元素
	slice = append(slice, 10, 11)
	slice[0] = 1314
	fmt.Println("切片添加元素后数组的值:", a, "\n切片添加元素后切片的值:", slice)
	fmt.Println("-------------------------------------")

输出

改变前数组的值: [0 1 2 3 4 5 6 7 8 9] 
改变前切片的值: [0 1 2 3 4 5 6 7 8 9]
-------------------------------------
改变后数组的值: [0 1 2 3 4 105 6 17 8 9] 
改变后切片的值: [0 1 2 3 4 105 6 17 8 9]
-------------------------------------
切片添加元素后数组的值: [0 1 2 3 4 105 6 17 8 9] 
切片添加元素后切片的值: [1314 1 2 3 4 105 6 17 8 9 10 11]
-------------------------------------

在这里,我们给切片增加新的元素,用到append()方法,切片添加元素后,改变了第一个元素的值,但并不会该变数组a的元素值,这是为什么呢?详细看append()方法的讲解


那么数组a的改变会改变切片?

a := [10]int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
	slice := a[:]
	fmt.Println("改变前数组的值:", a, "\n改变前切片的值:", slice)
	fmt.Println("-------------------------------------")

	slice[5] += 100
	slice[7] += 10
	fmt.Println("改变后数组的值:", a, "\n改变后切片的值:", slice)
	fmt.Println("-------------------------------------")

	a[0] = 520
	fmt.Println("改变后数组的值:", a, "\n改变后切片的值:", slice)

输出

改变前数组的值: [0 1 2 3 4 5 6 7 8 9] 
改变前切片的值: [0 1 2 3 4 5 6 7 8 9]
-------------------------------------
改变后数组的值: [0 1 2 3 4 105 6 17 8 9] 
改变后切片的值: [0 1 2 3 4 105 6 17 8 9]
-------------------------------------
改变后数组的值: [520 1 2 3 4 105 6 17 8 9] 
改变后切片的值: [520 1 2 3 4 105 6 17 8 9]

答案是会的,这是基于切片还是指向于数组a的情况

append()

切片添加元素的时候,元素个数 > cap

在这里,我们用上面例子的代码

a := [10]int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
	slice := a[:]
	fmt.Println("改变前数组的值:", a, "\n改变前切片的值:", slice)
	fmt.Println("-------------------------------------")

	slice[5] += 100
	slice[7] += 10
	fmt.Println("改变后数组的值:", a, "\n改变后切片的值:", slice)
	fmt.Println("-------------------------------------")

	// 往切片中添加元素
	slice = append(slice, 10, 11)
	slice[0] = 1314
	fmt.Println("切片添加元素后数组的值:", a, "\n切片添加元素后切片的值:", slice)
	fmt.Println("-------------------------------------")

输出

改变前数组的值: [0 1 2 3 4 5 6 7 8 9] 
改变前切片的值: [0 1 2 3 4 5 6 7 8 9]
-------------------------------------
改变后数组的值: [0 1 2 3 4 105 6 17 8 9] 
改变后切片的值: [0 1 2 3 4 105 6 17 8 9]
-------------------------------------
切片添加元素后数组的值: [0 1 2 3 4 105 6 17 8 9] 
切片添加元素后切片的值: [1314 1 2 3 4 105 6 17 8 9 10 11]
-------------------------------------

在这里,增加切片的元素后,因为元素个数 > cap,所以指向的那个数组的容量不够,所以会重新创建一个新的数组,并把旧的元素转移过去,再把新元素追加在旧元素的最后,最后把切片的ptr指向新的数组

所以当元素个数 > cap的时候,切片的改变并不会改变数组a的元素,是因为切片指向了新的数组

以上这个例子是基于用数组创建切片的例子

切片添加元素的时候,元素个数 < cap

a := [10]int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
	slice := a[:7]
	fmt.Println("改变前数组的值:", a, "\n改变前切片的值:", slice)
	fmt.Println("-------------------------------------")

	slice[5] += 100
	slice[6] += 10
	fmt.Println("改变后数组的值:", a, "\n改变后切片的值:", slice)
	fmt.Println("-------------------------------------")

	// 往切片中添加元素
	slice = append(slice, 10, 11)
	slice[0] = 1314
	fmt.Println("切片添加元素后数组的值:", a, "\n切片添加元素后切片的值:", slice)
	fmt.Println("-------------------------------------")

输出

改变前数组的值: [0 1 2 3 4 5 6 7 8 9] 
改变前切片的值: [0 1 2 3 4 5 6]
-------------------------------------
改变后数组的值: [0 1 2 3 4 105 16 7 8 9] 
改变后切片的值: [0 1 2 3 4 105 16]
-------------------------------------
切片添加元素后数组的值: [1314 1 2 3 4 105 16 10 11 9] 
切片添加元素后切片的值: [1314 1 2 3 4 105 16 10 11]
-------------------------------------

从输出可以看出,因为切片添加元素的个数后并没有超过cap的个数,所以切片添加新的新的元素后,切片的ptr还是指向数组a

不过这很奇怪,为什么改变的值的位置很奇怪呢? 在这里插入图片描述 从这图可以看出,slice := a[:7] 它所指向的范围就是0-6的这些元素,后面我们使用append()方法后,往里面添加两个元素,添加的时候,元素的个数 < cap,所以切片还是指向当前的数组a,只是把指向的范围扩大了,添加的新值覆盖了数组a的值 在这里插入图片描述 切片除了添加元素之外,还可以在切片中加入一个新的切片

通过切片创建切片

a := [10]int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
	slice := a[:]
	slice2 := slice[1:5]
	fmt.Println("slice的值:", slice, "\nslice2的值:", slice2)
	fmt.Println("-----------------")
	slice2[0] = 520
	fmt.Println("改变slice2的值后slice的值:", slice, "\n改变slice2的值后slice2的值:", slice2)

输出

slice的值: [0 1 2 3 4 5 6 7 8 9] 
slice2的值: [1 2 3 4]
-----------------
改变slice2的值后slice的值: [0 520 2 3 4 5 6 7 8 9] 
改变slice2的值后slice2的值: [520 2 3 4]

从输出来看,从切片中创建切片,有时候改变新切片的值,是会影响原来切片的值的,这是为啥呢?这个可以看上面的数组和切片,原理是一样的。

copy()

copy的参数变量名并不是source和target,这样只是为了让你们更好的理解,哪边是被复制的,哪边是要复制的

copy(target, source)

代码示例

a := [10]int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
slice := a[:]
slice2 := make([]int, 5)
fmt.Println("slice的值为:", slice, "slice2的值为:", slice2)
copy(slice2, slice)
fmt.Println("复制后slice的值为:", slice, "复制后slice2的值为:", slice2)

输出

slice的值为: [0 1 2 3 4 5 6 7 8 9] slice2的值为: [0 0 0 0 0]
复制后slice的值为: [0 1 2 3 4 5 6 7 8 9] 复制后slice2的值为: [0 1 2 3 4]

从输出来看,可以知道,copy的第一个值,代表了你要复制的切片,第二个值是被复制的切片。通俗点就是说,左边的参数是接收,右边的参数是发送,这样就比较好理解了。

至于要复制多少个参数给对方,取决于len(发送切片)和len(接收切片)最小的那个值


欢迎大家关注下个人的「公众号」:独醉贪欢