内容简介:数组的长度在定义后无法再次修改,数组是值类型,每次传递都会产生一份副本,显然这种数据结构无法满足正式开发的需求,为此golang提供了数组切片数组切片就像是一个指向数组的指针,数组切片有自己的数据结构,而不仅是一个指针。切片可以通过
数组的长度在定义后无法再次修改,数组是值类型,每次传递都会产生一份副本,显然这种数据结构无法满足正式开发的需求,为此golang提供了数组切片
数组切片就像是一个指向数组的指针,数组切片有自己的数据结构,而不仅是一个指针。
切片的定义
切片可以通过 make() 创建 格式 make([]type , 长度 ,容量) ,如:
a := make([]int, 10, 10)
上面演示了创建一个类型为 int ,长度为10的切片
slice是可变长的,长度表示的是数组的出始长度,容量表示slice可以容纳的元素容量,容量未设置时,默认容量等于数组长度,slice的长度和容量分别可以使用 len() 与 cap() 获得
slice也可以使用 数组 来生成 格式 array[start:end]
- start表示从数组什么位置开始截取 省略为从0开始
- end为结束位置的索引 不包含end索引本身 省略为一直到数组尾部 如
var b = [3]string{"a", "b", "c"}
slice0 := b[0:1]
slice1 := b[:2]
slice2 := b[1:]
fmt.Println(slice0, slice1, slice2)
切片还可以由切片生成
var a = [10]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
b := a[5:]
c := b[2:]
fmt.Println(b, c)
切片是引用类型
slice本身不是数组 它指向底层数组 切片是一个引用类型 改变切片将改变原始数组中的值 如下列这段程序
var a [10]int
b := a[5:]
fmt.Println("切片元素赋值前的原始数组为:", a)
b[0] = 123
b[1] = 456
b[2] = 789
fmt.Println("切片元素赋值后的原始数组为:", a)
上面的程序会输出:
切片元素赋值前的原始数组为: [0 0 0 0 0 0 0 0 0 0] 切片元素赋值后的原始数组为: [0 0 0 0 0 123 456 789 0 0]
注意:我们取的是a中下标为5以后的元素生成切片,但是,生成的切片,会重置下标,也就是说虽然我们的切片的元素为 a[5] a[6] .. a[9] 但是元素的下标是从0开始的,而当多个slice指向同一个底层数组,一个slice改变,所有的slice都会改变
var a = [5]string{"a", "b", "c", "d", "e"}
slice1 := a[:3]
slice2 := a[2:]
fmt.Println(slice1, slice2)
lice1[2] = "ffff"
//slice2[0] = "ffff"
fmt.Println(a, slice1, slice2)
关于容量
数组(切片)在内存中为一段连续的地址
append()主要用于给某个切片追加元素
我们定义一个容量为5的切片 再给它追加元素
var a = []int{1, 2, 3, 4, 5}
b := append(a, 6)
fmt.Println(b, "长度:", len(b), "容量:", cap(b))
上述结果会输出 [1 2 3 4 5 6] 长度: 6 容量: 10 ,如果该切片容量cap足够 就直接追加长度len变长,如果空间不足,就会重新开辟内存 并将之前的元素和新的元素一同拷贝进去,重新开辟的容量一般为原始长度的两倍,使用过 mongodb 的应该对这个不陌生 与mongodb中的文档拷贝类似,再来看几个有趣的例子, append() 可以一起追加多个元素或一个切片
c := append(a, 6, 7, 8, 9, 10, 11)
//c := append(a, []int{6, 7, 8, 9, 10, 11}...)
fmt.Println(c, "长度:", len(c), "容量:", cap(c))
我们会觉得输出的内容为 [1 2 3 4 5 6 7 8 9 10 11] 11 20 , 而结果是 [1 2 3 4 5 6 7 8 9 10 11] 长度: 11 容量: 12 , 不是说增加两倍么?为什么容量只增加了1个呢?,append增加元素的时候是一个一个来的,所以他会每次增加插入数量两倍的容量,也就是2,有兴趣的可以自己测试一下.
切片拷贝
上述过程中就发生了重新开辟类型和拷贝,整个过程如图所示,显然,当增加元素的时候,切片容量不够,所以需要扩充,可是后边的内存被其他变量使用了,无法使用,这时候golang就会新开辟一段连续的内存 将原来的内容拷贝进去 我们上边说过 切片是引用类型 那么这种情况下会发生什么呢?
var a = [5]int{1, 2, 3, 4, 5}
b := make([]int, 2, 3)
b = a[:2]
//给切片b增加元素
c := append(b, 123456789)
fmt.Println(a, b, c)
操作影响了底层数组a
再定义一个切片d 这次容量给2
var a = [5]int{1, 2, 3, 4, 5}
d := a[:2]
fmt.Println(a, d, append(d, 333, 4444, 55555))
为什么没有影响底层数组呢? 这就是上边说的, 如果容量不够的时候会发生拷贝操作, 新生成的切片与原始切片和数组之间就不再有引用关系了
关于容量,定义时要考虑两点:
- 分配足够大的容量会减少切片复制的情况,会造成内存浪费
- 不指定容量,增加元素时,会发送切片拷贝,又会造成性能开销,失去与底层数组的引用关系
所以,是否要指定容量,指定多大,是与你的实际业务情况有很大关系的
删除元素
golang中没有提供直接删除切片中元素的方法,但我们可以用户一种替代方案,我要删除b中的 b[2]
var a = [10]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
b := a[5:]
c := append(b[:2], b[3:]...)
fmt.Println(c)
切片拷贝
切片拷贝通过 copy() 方法来完成
var a = []int{1, 2, 3, 4, 5, 6}
var b = []int{7, 8, 9}
copy(a, b)
fmt.Println(a)
copy(a,b) 将b中的元素赋值到a中 a中的元素将被按照b中元素的顺序替换
如果a的元素数量小于b 按顺序全部替换 如果a中元素大于b 按顺序部分替换
也可以通过下标的方式指定 如:用b替换a中后三个元素
copy(a[3:], b)
切片的遍历
切片与数组一致 也可以使用 for...range 遍历
var a = [10]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
b := a[5:]
for key, value := range b {
fmt.Println("key:", key, "value:", value)
}
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
The Four
Scott Galloway / Portfolio / 2017-10-3 / USD 28.00
NEW YORK TIMES BESTSELLER USA TODAY BESTSELLER Amazon, Apple, Facebook, and Google are the four most influential companies on the planet. Just about everyone thinks they know how they got there.......一起来看看 《The Four》 这本书的介绍吧!