在前面的文章中,我们简单的介绍了数组,但没说数组的缺点是什么?数组有个缺点就是初始化之后数组的长度就固定了,不能改变。但有时,我们不能在一开始就很精确的知道,我们要存储多少个元素,这样的话,对初始化数组的时候,就不好去给定个初始长度,给少了不行,给太多了又很浪费。
在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(接收切片)最小的那个值
欢迎大家关注下个人的「公众号」:独醉贪欢