设为首页 加入收藏

TOP

细说Go语言切片(一)
2018-11-29 12:08:33 】 浏览:33
Tags:细说 语言 切片

目录

在上一篇文章中已经了解了数组,数组有特定的用处,但是却有一些呆板(数组长度固定不可变),所以在 Go 语言的代码里并不是特别常见。接下来聊聊切片(slice),相对的,切片却是随处可见的,Go语言切片是一种建立在数组类型之上的抽象,它构建在数组之上并且提供更强大的能力和便捷。

内部实现

切片(slice)是对数组一个连续片段的引用(该数组我们称之为相关数组,通常是匿名的),所以切片是一个引用类型(因此更类似于 C/C++ 中的数组类型)。这个片段可以是整个数组,或者是由起始和终止索引标识的一些项的子集。需要注意的是,终止索引标识的项不包括在切片内。切片提供了一个相关数组的动态窗口。

切片是可索引的,并且可以由 len() 函数获取长度。

给定项的切片索引可能比相关数组的相同元素的索引小。和数组不同的是,切片的长度可以在运行时修
改,最小为 0 最大为相关数组的长度:切片是一个 长度可变的数组

切片对象非常小,是因为它是只有3个字段的数据结构:一个是指向底层数组的指针,一个是切片的长度,一个是切片的容量。这3个字段,就是Go语言操作底层数组的元数据,有了它们,我们就可以任意的操作切片了。

切片提供了计算容量的函数 cap() 可以测量切片最长可以达到多少:它等于切片的长度 + 数组除切片之外的长度。如果 s 是一个切片, cap(s) 就是从 s[0] 到数组末尾的数组长度。切片的长度永远不会超过它的容量,所以对于 切片 s 来说该不等式永远成立: 0 <= len(s) <= cap(s)

多个切片如果表示同一个数组的片段,它们可以共享数据;因此一个切片和相关数组的其他切片是共享存储的,相反,不同的数组总是代表不同的存储。数组实际上是切片的构建块。

优点 因为切片是引用,所以它们不需要使用额外的内存并且比使用数组更有效率,所以在 Go 代码中 切片比数组更常用。

声明切片

声明切片的方式和声明数组的方式差不都

//   变量名            变量类型
var variable_name = []var_type

如果切片只声明而没有初始化,那么这个切片的默认值为nil,长度为 0。

package main

import "fmt"

func main() {

    var sli []int
    if sli == nil {
        fmt.Println("sli和nil相等")
    } else {
        fmt.Println("sli和nil不相等")
    }
}

初始化数组

make方式创建切片,对应用类型的数据都可以使用make函数创建,该函数会返回该类型的引用。

slice := make([]int, 10, 20)

这里我们创建了一个类型为[]int,长度为10,容量为20的切片,如果不指定切片的容量,例如slice := make([]int, 10),那么该切片的容量和长度相等。

因为切片的底层是数组,所以创建切片时,如果不指定字面值的话,默认值就是数组的元素的零值。这里我们所以指定了容量是20,但是我们职能访问10个元素,因为切片的长度是10,剩下的10个元素,需要切片扩充后才可以访问。

容量必须>=长度,我们是不能创建长度大于容量的切片的。

还有一种创建切片的方式,是使用字面量,就是指定初始化的值。

slice := []int{1,2,3,4,5}

通过字面量创建切片和创建数组的方式非常像,只不过不用指定[]中的值([]里面没有...),这时候切片的长度和容量是相等的,并且会根据我们指定的字面量推导出来。当然我们也可以像数组一样,只初始化某个索引的值:

slice := []int{4:1}

这是指定了第5个元素为1,其他元素都是默认值0。这时候切片的长度和容量也是一样的。这里再次强调一下切片和数组的微小差别。

//数组
array := [...]int{4:1}
//切片
slice := []int{4:1}

切片还有nil切片和空切片,它们的长度和容量都是0,但是它们指向底层数组的指针不一样,nil切片意味着指向底层数组的指针为nil,而空切片对应的指针是个地址。

//nil切片
var slice []int
//空切片
slice:=[]int{}

nil切片表示不存在的切片,而空切片表示一个空集合,它们各有用处。

切片另外一个用处比较多的创建是基于现有的数组或者切片创建。

slice := []int{1, 2, 3, 4, 5}
slice1 := slice[:]
slice2 := slice[0:]
slice3 := slice[:5]

fmt.Println(slice1)
fmt.Println(slice2)
fmt.Println(slice3)

基于现有的切片或者数组创建,使用[i:j]这样的操作符即可,她表示以i索引开始,到j索引结束,截取原数组或者切片,创建而成的新切片,新切片的值包含原切片的i索引,但是不包含j索引。

i如果省略,默认是0;j如果省略默认是原数组或者切片的长度,所以例子中的三个新切片的值是一样的。这里注意的是ij都不能超过原切片或者数组的索引。

slice := []int{1, 2, 3, 4, 5}
newSlice := slice[1:3]

newSlice[0] = 10
    
fmt.Println(slice)
fmt.Println(newSlice)

fmt.Printf("%p\n", &slice[1])
fmt.Printf("%p\n", &newSlice[0])

这个例子证明了,新的切片和原切片共用的是一个底层数组,所以当修改的时候,底层数组的值就会被改变,所以原切片的值也改变了。当然对于基于数组的切片也一样的。

我们基于原数组或者切片创建一个新的切片后,那么新的切片的大小和容量是多少呢?这里有个公式:

对于底层数组容量是k的切片slice[i:j]来说
长度:j-i
容量:k-i

比如我们上面的例子slice[1:3],长度就是3-1=2,容量是5-1=4。不过代码中我们计算的时候不用这么麻烦,因为Go语言为我们提供了内置的lencap函数来计算切片的长度和容量。

slice := []int{1, 2, 3, 4, 5}
newSlice := slice[1:3]

fmt.Printf("newSlice长度:%d,容量:%d",len(newSlice),cap(newSlice))

以上基于一个数组或者切片使用2个索引创建新切片的方法,此外还有一种3个索引的方法,第3个用来限定新切片的容量,其用法为slice[i:j:k]

slice := []int{1, 2, 3, 4, 5}
newSlice := slice[1:2:3]

这样我们就创建了一个长度为2-1=1,容量为3-1=2的新切片,不过第三个索引,不能超过原切片的最大索引值5。

切片内存结构

var slice = []int{1, 2, 3, 4, 5}这样就创
编程开发网

首页 上一页 1 2 3 下一页 尾页 1/3/3
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇Go 语言实践(一) 下一篇Go标准库:深入剖析Go template

评论

帐  号: 密码: (新用户注册)
验 证 码:
表  情:
内  容:

array(4) { ["type"]=> int(8) ["message"]=> string(24) "Undefined variable: jobs" ["file"]=> string(32) "/mnt/wp/cppentry/do/bencandy.php" ["line"]=> int(214) }