The Way To Go --- 切片

书旅 · · 355 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

初识切片

基本使用

在go语言中一般不使用数组,大多都是使用切片(slice),看一个切片的示例


arr := [...]int{0,1,2,3,4,5,6,7,8}

s := arr[2:6]

这里定的s就是切片,上边的arr是一个数组,这里s的结果是[2,3,4,5](取arr中3到5这个区间的数据,是左闭右开的)

切片还有很多的用法,如下:


arr := [...]int{0,1,2,3,4,5,6,7,8}

fmt.Println("arr[2:6] = ", arr[2:6])

fmt.Println("arr[:6] = ", arr[:6])

fmt.Println("arr[2:] = ", arr[2:6])

fmt.Println("arr[:] = ", arr[:])

输出结果:


arr[2:6] = [2 3 4 5]

arr[:6] = [0 1 2 3 4 5]

arr[2:] = [2 3 4 5]

arr[:] = [0 1 2 3 4 5 6 7 8]

像这种方括号中([])加个冒号(:)的数据结构,都可以看做是切片(slice)。当然还有很多其它的声明方法

切片是对数组的view

我们知道在go中,数组是一个值类型,而切片是一个引用类型,切片是对数组的视图(view),可能不太好理解,看下边的代码


func updateSlice(s []int) {

s[0] = 520

}

func main() {

arr := [...]int{0,1,2,3,4,5,6,7,8}

s1 := arr[2:]

fmt.Println("s1更新之前")

fmt.Println("s1 = ", s1)

fmt.Println("arr = ", arr)

fmt.Println("更新切片s1")

updateSlice(s1)

fmt.Println("更新之后,s1和arr的值")

fmt.Println(s1)

fmt.Println(arr)

}

输出结果:


s1更新之前

s1 = [2 3 4 5 6 7 8]

arr = [0 1 2 3 4 5 6 7 8]

更新切片s1

更新之后,s1和arr的值

[520 3 4 5 6 7 8]

[0 1 520 3 4 5 6 7 8]

因为s1(切片)是对arr(数组)的视图(view),所以当s1中的值被改变之后,数组中的值也发生了改变(改变的是s1中下标为0的元素,对应的是数组arr中下标为2的元素)

对切片进行切片操作


func learnSlice1() {

arr := [...]int{0,1,2,3,4,5,6,7,8}

s1 := arr[2:6]

s2 := s1[1:]

fmt.Println("s1 = ", s1)

fmt.Println("s2 = ", s2)

}

输出结果:


s1 = [2 3 4 5]

s2 = [3 4 5]

s2是对切片s1又进行了一次切片

深度理解切片

直接看一个示例


func learnSlice2() {

arr := [...]int{0,1,2,3,4,5,6,7,8}

s1 := arr[2:6]

s2 := s1[3:5]

fmt.Println("s1 = ", s1)

fmt.Println("s2 = ", s2)

}

s1的打印结果,我们肯定知道,但是这里的s2打印结果是什么?

我们知道s1 = [2,3,4,5],s2是对s1取下标是3到5这个区间的数据,也就是s1[3]、s1[4]。但是可以看到s1的下标最大是s[3],那这里s1[3:5]会报错吗?

答案是不会报错,可以看一下打印的结果,然后再解释为什么


s1 = [2 3 4 5]

s2 = [5 6] //s1[3]、s1[4]

可以看到s2中的那个6就不在s1里边,如果我们尝试直接打印s1[4]会报错。但是打印s2这个切片,就会正常打印出结果,那到底这个6是如何取出来的?

上边有说切片(slice)是对数组(array)的视图(view),那这个底层是什么样的,如何view的?

上边给出的基础数组arr是[0,1,2,3,4,5,6,7,8],然后s1 := arr[2:6],取到的就是[2,3,4,5],这四个数字映射到s1的下标就是0、1、2、3,s1里边因为是对底层数组arr的view,它的下标不是到3就结束了,它还有4、5的,但是直接通过s1[4]或s1[5]是取不到的,但是s1还是知道它里边有4和5这两个下标的,因为s1是知道底层的数组的

然后,s2 := s1[3:5],取到的就是s1中3,4这两个下标的值。s2的下标就是0、1、2(2其实是看不见的)。从底层数组的角度s1[3]、s1[4]的值就是5和6

切片内部实现

切片中首先有一个ptr指针,它指向slice开始的那个元素。有一个len,标记的是切片的长度,我们使用下标的方式对切片取值的时候,最大只能取到下标为len-1的值,如果大于或等于len,就会报错。然后它里边还有一个值cap,它代表的是整个数组从ptr指向的位置开始到结束的长度

切片它可以向后扩展,但是不可以向后扩展。比如s1:=arr[2:6],s1最前边只能看到2,前边的0、1下标对应的值是看不到的

切片的下标不可以超过len(s),向后扩展不可以超过底层数组cap(s)

看一波示例


func learnSlice2() {

arr := [...]int{0,1,2,3,4,5,6,7,8}

s1 := arr[2:6]

s2 := s1[3:5]

fmt.Printf("s1 = %v, len(s1) = %d, cap(s1) = %d", s1, len(s1), cap(s1))

fmt.Println()

fmt.Printf("s2 = %v, len(s2) = %d, cap(s2) = %d", s2, len(s2), cap(s2))

}

输出结果


s1 = [2 3 4 5], len(s1) = 4, cap(s1) = 7

s2 = [5 6], len(s2) = 2, cap(s2) = 4


有疑问加站长微信联系(非本文作者)

本文来自:Segmentfault

感谢作者:书旅

查看原文:The Way To Go --- 切片

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

355 次点击  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传