内容简介:1.起始位置:切片引用数组的开始位置。2.大小:切片中的元素个数。切片中的大小不能超过容量数量。可以使用len()函数对切片统计大小。3.容量:切片最大可存的元素个数。如果空间不足以容纳足够多的元素,切片就会进行动态“扩容”,此时新切片的长度会发生改变。一般切片的扩容是按照扩容前容量的2倍。可以使用cap()函数对切片容量进行统计。
1.起始位置:切片引用数组的开始位置。
2.大小:切片中的元素个数。切片中的大小不能超过容量数量。可以使用len()函数对切片统计大小。
3.容量:切片最大可存的元素个数。如果空间不足以容纳足够多的元素,切片就会进行动态“扩容”,此时新切片的长度会发生改变。一般切片的扩容是按照扩容前容量的2倍。可以使用cap()函数对切片容量进行统计。
切片与数组的区别
1.切片是对数组中的连续引用。切片的初始位置指向数组的内存地址,如果切片的值改变,数组对应的值也会对应改变。
2.切片的长度是动态的,本质上是一个可变的动态数组。数组的长度在定义的时候就决定好了,后期是无法修改数组的长度的。
3.切片的长度是可以动态扩容的[如上面容量一次提到的]。
切片内存分布
切片的定义
通过数组定义
array[startIndex:endIndex]
1.array:被切片定义的数组名称。
2.startIndex:切片的开始位置。
3.endIndex:切片的结束位置。结束位置不包含在内的。
array := []string{"A", "B", "C", "D", "E", "F", "G", "H", "I", "G", "K", "L"} slice := array[2:5]
从数组或切片生成新的切片拥有如下特性:
1.取出的元素数量为:结束位置 - 开始位置。
2.取出元素不包含结束位置对应的索引,切片最后一个元素使用 slice[len(slice)] 获取。
3.当缺省开始位置时,表示从连续区域开头到结束位置。
4.当缺省结束位置时,表示从开始位置到整个连续区域末尾。
5.两者同时缺省时,与切片本身等效。
6.两者同时为0时,等效于空切片,一般用于切片复位。
直接创建切片
var name []type
1.name:切片名称。
2.type:切片类型。
// 直接申明切片 var sliceName1 []string // 直接申明一个空切片 var sliceName2 = []int{} // 申明一个值为空的切片 var sliceName3 = []int{1, 2, 3} // 申明一个切片,并初始化值 if sliceName1 == nil { fmt.Println("sliceName1是空切片") } if sliceName2 == nil { fmt.Println("sliceName2是空切片") } if sliceName3 == nil { fmt.Println("sliceName3是空切片") } fmt.Println(sliceName1, sliceName2, sliceName3)
// 输出内容 sliceName1是空切片 [] [] [1 2 3]
因为sliceName2在定义时,给初始化了一个空值。虽然该切片的内容是空,但实际是分配过内存了。如下演示代码:
// 测试三个指针的内存地址 fmt.Printf("%p\t%p\t%p", sliceName1, sliceName2, sliceName3) 0x0 0x1195a98 0xc00008a020
使用make创建指针
make([]type,size,cap)
1.type 是指切片的元素类型。
2.size 指的是为这个类型分配多少个元素。
3.cap 为预分配的元素数量,这个值设定后不影响 size,只是能提前分配空间,降低多次分配空间造成的性能问题。
使用 make() 函数生成的切片一定发生了内存分配操作,但给定开始与结束位置(包括切片复位)的切片只是将新的切片结构指向已经分配好的内存区域,设定开始与结束位置,不会发生内存分配操作。
// 使用make方式创建切片 slice1 := make([]string, 2, 3) slice1[0] = "1" fmt.Println(slice1) slice2 := make([]string, 2, 3) fmt.Println(slice2) fmt.Println("切片的长度为", len(slice1)) fmt.Println("切片的容量为", cap(slice1))
// 输出结果 [1 ] [ ] 切片的长度为 2 切片的容量为 3
当切片未给对应的下标赋值时,会根据定义的切片类型初始化一个值。
切片的迭代
切片从本质上来讲,是一个动态的数组,因此可以直接使用for循环进行迭代即可。
// 使用for迭代切片 slice := []int{1, 2, 3, 4} for index, value := range slice { fmt.Println("切片slice的index,value分别为", index, value) }
// 输出结果 切片slice的index,value分别为 0 1 切片slice的index,value分别为 1 2 切片slice的index,value分别为 2 3 切片slice的index,value分别为 3 4
切片与数组的区别
// 通过数组定义切片 var array1 = [3]int{1, 1, 3} fmt.Println("数组的元素分别是:", array1) slice1 := array1[0:2] slice1[1] = 11111 fmt.Println("数组的元素分别是:", array1)
// 输出结果 数组的元素分别是: [1 1 3] 数组的元素分别是: [1 11111 3]
操作切片
追加切片
append(切片,追加元素)
1.尾部追加
// 1.默认是向切片的尾部追加元素 slice8 := make([]int, 2, 3) fmt.Println("slice8:", slice8) fmt.Println("slice8的容量是:", cap(slice8)) // 追加多个元素,手动解包 slice8 = append(slice8, 3, 4) fmt.Println(slice8) // 这里需要使用...,切片需要进行解包 slice8 = append(slice8, []int{1, 2, 3, 4}...) fmt.Println(slice8) // 当切片的追加容量超过初始容量,切片会自动进行扩容,扩容的以2倍增长。 fmt.Println("slice8的容量是:", cap(slice8))
slice8: [0 0] slice8的容量是: 3 [0 0 3 4] [0 0 3 4 1 2 3 4] slice8的容量是: 12
2.头部添加
// 头部添加 slice8 = append(make([]int, 2, 3), slice8...) fmt.Println("向头部追加元素", slice8)
在切片开头添加元素一般都会导致内存的重新分配,而且会导致已有元素全部被复制1次。因此,从切片的开头添加元素的性能要比从尾部追加元素的性能差很多。
// 测试向头部添加,切片内存地址变化 slice8 := make([]int, 2, 3) fmt.Printf("切片内存地址:%p", slice8) slice8 = append(make([]int, 1, 3), slice8...) fmt.Println("追加切片元素", slice8) fmt.Printf("切片内存地址:%p", slice8)
// 输出结果 切片内存地址:0xc00008a0e0 追加切片元素 [0 0 0] 切片内存地址:0xc00008a100
3.中间添加
a.每个添加操作中的第二个append调用都会创建一个临时切片,并将 a[i:] 的内容复制到新创建的切片中,然后将临时创建的切片再追加到 a[:i] 中。
b.基本公式:append(sliceName[0:x], append(要追加的切片, sliceName[x:]…)…)向x位置追加一个切片。
//向切片的中间位置添加元素 slice9 := make([]int, 3, 10) fmt.Println("slice9", slice9) // 在第2个位置追加一个大小为3的切片 slice9 = append(slice9[0:2], append([]int{1, 2, 3}, slice9[2:]...)...) fmt.Println(slice9)
删除切片
Go语言并没有对删除切片元素提供专用的语法或者接口,需要使用切片本身的特性来删除元素,根据要删除元素的位置有三种情况,分别是从开头位置删除、从中间位置删除和从尾部删除,其中删除切片尾部的元素速度最快。切片的删除,发生在切片内部指针的移动,切片的内存地址是不会发生变化。
1.头部删除
// 切片头部删除 slice1 := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11} fmt.Println("slice1切片的元素分配为:", slice1) fmt.Printf("%p\n", &slice1) // a.直接删除 slice1 = slice1[3:len(slice1)] fmt.Printf("%p\n", &slice1) // b.使用copy删除 slice1 = slice1[:copy(slice1, slice1[3:])] // c.使用append删除 slice1 = append(slice1[:0], slice1[3:]...) fmt.Println("删除后的切片为:", slice1)
// 输出内容 slice1切片的元素分配为: [1 2 3 4 5 6 7 8 9 10 11] 0xc00000c080 0xc00000c080 删除后的切片为: [4 5 6 7 8 9 10 11]
2.中间删除
// 中间删除切片 // a.使用append删除 slice1 = append(slice1[:3], slice1[4:]...) // b.使用copy删除 slice1 = slice1[:3+copy(slice1[3:], slice1[4:])]
// 输出结果 slice1切片的元素分配为: [1 2 3 4 5 6 7 8 9 10 11] 0xc00000c080 删除后的切片为: [1 2 3 5 6 7 8 9 10 11]
3.尾部删除
slice1切片的元素分配为: [1 2 3 4 5 6 7 8 9 10 11] 0xc00007c020 删除后的切片为: [1 2 3 4 5 6 7 8]
// 输出内容为 slice1切片的元素分配为: [1 2 3 4 5 6 7 8 9 10 11] 0xc00007c020 删除后的切片为: [1 2 3 4 5 6 7 8]
通过发现,这里有一个固定的公式,这里的3可以改为N,即代表删除N个元素。上面的copy和append方式一致。
复制切片
Go切片复制可以使用内置的copy()函数将一个数组切片复制到另一个数组切片中,如果加入的两个数组切片不一样大,就会按照其中较小的那个数组切片的元素个数进行复制。
copy(desSlice, srcSlice) int
copy()函数返回的是切片复制的个数。
// 切片复制 slice10 := make([]int, 5, 10) slice11 := []int{1, 2, 3, 4} slice12 := slice10 num := copy(slice11, slice10) fmt.Println("slice11", slice11) fmt.Printf("%p,%p,%p", &slice10, &slice11, &slice12) fmt.Println(num)
切片的引用,被引用的切片发生改变,引用的切片也会发生改变。切片的赋值,原切片发生变动,赋值的切片不会变动。
// 打印结果 slice11 [0 0 0 0] 0xc00007c180,0xc00007c1a0,0xc00007c1c04 引用切片的值 999 复制切片的值 0 999 4 5 2 3 4 slice11 [0 0 0 0] 0xc00007c200,0xc00007c220,0xc00007c2404
以上所述就是小编给大家介绍的《Go切片》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
TensorFlow:实战Google深度学习框架(第2版)
顾思宇、梁博文、郑泽宇 / 电子工业出版社 / 2018-2-1 / 89
TensorFlow是谷歌2015年开源的主流深度学习框架,目前已得到广泛应用。《TensorFlow:实战Google深度学习框架(第2版)》为TensorFlow入门参考书,旨在帮助读者以快速、有效的方式上手TensorFlow和深度学习。书中省略了烦琐的数学模型推导,从实际应用问题出发,通过具体的TensorFlow示例介绍如何使用深度学习解决实际问题。书中包含深度学习的入门知识和大量实践经......一起来看看 《TensorFlow:实战Google深度学习框架(第2版)》 这本书的介绍吧!