Go常见错误集锦之slice数据结构趣味文答
2023-4-24 08:53:52 Author: Go语言中文网(查看原文) 阅读量:22 收藏

本篇文章以问答的形式对 slice 做一个总结,以帮助大家能够整体的理解和掌握slice结构。

其中指定了两个角色,渔夫子和百晓生。渔夫子是Go学堂的老先生,百晓生是Go学堂的小学童。
以下是渔夫子和小学童百晓生的有趣问答。
渔夫子:百晓生,golang中的slice是什么,它有什么特点?
百晓生:在Golang中,slice是一种可保存一组相同数据类型的数据结构。它和数组的区别是slice的长度和容量是可变的,可以随时往slice中通过append函数增加元素。
渔夫子:slice的底层数据结构是什么样的?
百晓生:slice的底层结构其实是有一个sliceHeader头,包含len、cap和data指针。data指针指向一个底层的数组。如下:
type sliceHeader struct {  Data uintptr  Len int  Cap int}

渔夫子:晓生,你知道slice中为什么会有length和cap两个字段,各自的应用场景是什么?
百晓生:slice的length是slice的可见区域,即一个slice的变量只能看到length个元素,即使容量cap大于length,多余的cap对slice也是不可见的。但cap一般是用于在使用append给slice添加元素时用的。当往slice中append元素时,length的长度就会改变,同时如果容量cap已经被分配,则直接添加就好,不用再重新分配新的空间。

渔夫子:晓生,那你知道初始化切片的方式都有哪些吗?
百晓生:这还不简单。可以通过以下几种:
//方式一var s []int//通过make的方式var s2 = make([]int010)//通过组合字面量方式var s3 = []int{123}

渔夫子:嗯,不错。那你知道他们之间的区别吗?
百晓生:sure。第一种方式是初始化了一个nil切片,第二种方式是初始化后,是容量为10,长度为0的切片。第三种方式是长度和容量都是3的切片,并且有初始化的值。
渔夫子:非常好。那什么又是空切片呢?nil切片又是什么呢?
百晓生:这个我也知道。空切片就是长度为0的切片。nil切片的长度也是0,所以是空切片的一个特例。但nil切片和非nil的空切片的不同在于nil切片的sliceHeader中的Data指针是nil,而非nil的空切片的Data指针是指向底层数组地址的。所以在判断是否为空切片时,最好是判断切片长度是否为0就可以了。这个你在之前的文章Go常见错误集锦之令人困惑的nil切片和空切片讲过的呀。
渔夫子:very good。那本夫子再问你,在使用make初始化切片时,有3个参数。后面两个参数为什么有时候只写1个,有时候又写2个呢?
百晓生:这个so easy。make的初始化是这样的:make([]int, length, cap),length是指定的切片的长度,也就是切片的可见元素有多少个。cap是指定的切片的底层数组的最大容量。cap主要是用于预先分配空间,在使用append对切片进行操作时,不用再为新元素重新分配空间了,这样就减少了分配内存的次数,提高了性能。这个难不住我的,嘿嘿。请参考 Go常见错误集锦之不正确的初始化slice方式会降低性能
渔夫子:既然提高了append操作。晓生,那你知道在使用append对slice进行操作时需要注意什么吗?
百晓生:这个...,有啥注意的。
渔夫子:哈哈,不知道了吧。那本夫子提醒你一下,在对slice进行切分操作后,再使用append对子切片进行操作....
百晓生:噢,对子切片呀。我记起来了。如果对一个切片进行切分后,两个切片的底层数据实际上指向同一个。如果再使用append对子切片进行操作,就有可能对原始的切片数据造成影响。所以可以使用copy获取子切片,或者在使用满切片的表达式来限制子切片的容量,以便在对子切片进行append操作时强制进行内存的重分配,这样就不影响最初的切片了。这节里Go常见错误集锦之append操作slice时的副作用中都讲过的呀。
渔夫子:copy,嗯,不错。那你又可曾知道,在使用copy函数时有哪些坑吗?
百晓生:哈哈,我当然不会掉坑了。在使用copy函数时,只要注意两个切片中的最小长度就行了。copy函数只会将min(len(dstslice), len(srcslice))个元素拷贝到dstslice中,有时候会是0个,所以这个要特别注意的。怎么样,难不倒我吧。
渔夫子:给你一个大大的赞。还有最后一个考题。我们都知道,计算机中的内存资源是有限的。那在使用slice的时候,一不小心就会使内存造成泄露。你知道是哪些场景吗?
百晓生:哎呀呀,这个也难不住我。上边提到过,因为在对已有的切片进行切分操作后,两个切片共享了底层的数组,所以,如果在操作一个大容量的切片时,只是切分出一个很小的子切片,那这个切片虽小,但容量依然是原切片的容量,所以如果大量使用的话,就会造成内存浪费,甚至泄露。要想解决这个也很简单,原则只有1个,就是不让子切片和原切片共享底层数组,切断他们的联系就行。请参考切片使用不当会造成内存泄漏的那些场景节。
渔夫子:哈哈,老夫子甚是满意啊。

推荐阅读

福利
我为大家整理了一份从入门到进阶的Go学习资料礼包,包含学习建议:入门看什么,进阶看什么。关注公众号 「polarisxu」,回复 ebook 获取;还可以回复「进群」,和数万 Gopher 交流学习。


文章来源: http://mp.weixin.qq.com/s?__biz=MzAxMTA4Njc0OQ==&mid=2651454289&idx=1&sn=74da4d512efc7234b7871c3b4f60d783&chksm=80bb25a3b7ccacb5470f0e2fc4b4fde28421be0e97f103f702837abb061b092c40d429bbda34#rd
如有侵权请联系:admin#unsafe.sh