大家好,我是炸鱼。前段时间有个提示,就是Go1.17将正式支持切片(Slice)到数据(Array)的转换,不再需要使用之前的方法,安全多了。不过,也有同学提出了新的质疑。在Go语言中,数组其实用得比较少,甚至有同学认为Go中可以去掉数组。数组相对于切片有什么优势,我们应该在什么场景下使用它们?这是我们需要深入探讨的问题,今天就和大家一起一探究竟。本文将首先简单介绍什么是数组和切片,然后进一步分析数组的使用场景。让我们一起愉快地开启熏鱼之路吧。什么是数组?Go语言中有一种基本数据类型,称为数组。它的格式是:[n]T。是一个包含N个T类型值的数组,基本的声明格式是:vara[10]int表示一个变量a被声明为一个包含10个整数的数组。数组的长度是其类型的一部分,因此不能任意调整数组的大小。在用法示例中:funcmain(){vara[2]stringa[0]="braininto"a[1]="friedfish"fmt.Println(a[0],a[1])fmt。println(a)primes:=[6]int{2,3,5,7,11,13}fmt.Println(primes)}输出结果:脑子是炸鱼[脑子是炸鱼][23571113]在赋值和访问方面,可以针对不同的索引分别操作数组。在内存布局中,数组的索引0和1...会在相邻的区域,可以直接访问。什么是切片以及为什么在业务代码中似乎很少使用数组。因为Go语言有slice数据类型:基本声明格式为:vara[]T表示变量a是一个带有type元素的sliceT。切片是通过指定两个用冒号分隔的索引(下限和上限)形成的:a[low:high]用法示例:funcmain(){primes:=[3]string{"Fryfish","engage","Go"}vars[]string=primes[1:3]fmt.Println(s)}输出结果:[Go]切片支持动态扩缩容,不需要用户关注,非常方便。更重要的是,切片本身的底层数据结构包含一个数组:typeslicestruct{arrayunsafe.Pointerlenintcapint}很多人开玩笑说数组在Go语言中已经可以下岗了,切片可以完蛋了。。.你怎么看这个说法,赶紧在脑子里想出答案。数组的优点介绍完数组和切片的基本场景,关于数组的优点,我们来看看官方readme:数组在规划内存的详细布局时很有用,有时可以帮助避免分配,但主要是它们是切片的构建块。非常粗略的间接:数组在规划内存的详细布局时很有用,有时可以帮助避免分配,但大多数情况下它们是切片的构建块。我们仔细看看官方的“密文”具体指的是什么。我们将密文解释为以下内容进行解释:可比较。编译安全。长度是类型。规划内存布局。访问速度。可比较数组的长度是固定的,可以相互比较。数组是值对象(不是引用或指针类型),不会遇到接口比较等误判:funcmain(){a1:=[3]string{"brain","in","friedfish"}a2:=[3]string{"炸鱼","in","大脑"}a3:=[3]string{"大脑","Enter","炸鱼"}fmt.Println(a1==a2,a1==a3)}输出结果:falsetrue另一方面,切片不能直接比较,也不能用于判断:funcmain(){a1:=[]string{"brain","in","炸鱼"}a2:=[]string{"炸鱼","in","大脑"}a3:=[]string{"大脑","in","炸鱼"}fmt.Println(a1==a2,a1==a3)}输出结果:#command-line-arguments./main.go:10:17:invalidoperation:a1==a2(slicecanonlybecomparetonil)./main.go:10:27:invalidoperation:a1==a3(slice只能和nil比较)数组可以作为map的k(keys),但不能作为slice,slice没有实现相等运算符(equalityoperator),需要考虑的问题很多,for例子:涉及浅层比较和深度比较。指向值比较的指针。如何处理递归类型。为结构和数组定义了相等性,因此此类类型可以用作映射键。没有切片的平等定义,存在非常根本的差距。数组是可比较和相等的,切片不能。编译安全数组可以通过在编译时检查索引范围来提供更高的编译时安全性。如下:s:=make([]int,3)s[3]=3//"Only"aruntimepanic:runtimeerror:indexoutofrangea:=[3]int{}a[3]=3//Compile-timeerror:invalidarrayindex3(outofboundsfor3-elementarray)这个编译检查的帮助虽然“小”,但其实意义很大。天天看到各种切片越界的告警,感觉能背下来。。。万一这个越界在热点路径上,影响大量用户,出事就背诵每分钟,3.25会再来。醒来不是梦吗?数组可以安全编译,而切片不能。长度是一个类型数组的长度是数组类型声明的一部分,所以不同长度的数组是不同的类型,两个不是一个“东西”。当然,这是一把双刃剑。它的优点是可以用来显式指定所需数组的长度。例如:您想在业务代码中编写一个使用IPv4地址的函数。可以声明类型[4]byte。使用数组具有以下效果:编译时保证传递给函数的值恰好有4个字节,不多也不少。如果长度不对,可以认为是无效的IPv4地址,非常方便。同时数组的长度也可以用来记录:MD5类型,在crypto/md5包中,md5.Sum方法返回一个[Size]byte类型的值,其中md5.Size的一个常量是16:MD5校验和长度。对于IPv4类型,声明的[4]byte正确记录有4个字节。对于RGB类型,[3]byte的声明表明每个颜色分量有1个字节。在具体的业务场景下,还是用数组比较好。规划内存布局数组可以让你更好地控制内存布局,因为你不能直接在有切片的结构中分配空间,你可以使用数组来解决它。例如:typeFoostruct{buf[64]byte}不知道大家有没有在一些Go图形库上看到过这种不清楚的操作,例子如下:typeTGIHeaderstruct{_uint16//Reserved_uint16//ReservedWidthuint32Heightuint32_[15]uint32//15"don'tcare"dwordsSaveTimeint64}因为业务需求,我们需要实现一种格式,这里的格式是"TGI"(理论上的GoImage),并且标头包含这样的字段:有2个保留字(每个16位)。图像宽度为1个字。图像高度为1个字。有15个业务“无关”字节。有1个保存时间,图像的保存时间为8字节,是从1970年1月1日UTC开始的纳秒数。这样看,就不难理解数组在这种场景下的优势了。定长、可控的内存,在规划内存布局时非常有用。访问速度使用数组时,访问(单个)数组元素比访问切片元素效率更高,时间复杂度为O(1)。例如:vara[2]stringa[0]="braininto"a[1]="friedfish"fmt.Println(a[0],a[1])切片不太方便,访问a某个位置的索引值需要:vara[]int{0,1,2,3,4,5}number:=numbers[1:3]比较复杂,删除指定索引位置的值,是可以的也有小伙伴折腾了半天,甚至在寻找第三方开源库来快速实现。无论是访问速度还是开发效率,数组都有一定的优势,是切片无法直接比较的。小结经过一轮讨论,我们对Go语言中的数组有了更深的理解。总结一下:数组是值对象,可以比较,可以用数组作为map的mapkey。而这些,切片是不允许的,不能比较,不能作为map的映射键。数组具有编译安全检查,可以在早期避免越界行为。切片是运行时的越界panic,阶段不一样。数组可以更好地控制内存布局。如果将它们替换为切片,你会发现你不能直接在有切片的结构中分配空间,但数组可以。在访问单个元素时,数组的性能优于切片。数组的长度,它是类型的一部分。它在特定情况下具有特定含义。数组是切片的基础,每个数组都可以是切片,但不是每个切片都可以是数组。如果值是固定大小的,你可以通过使用数组获得一个小的性能增益(至少节省切片头占用的空间)。是否符合你心目中的阵法优势?欢迎大家在评论区讨论交流。我是建宇,下期再见:)有什么问题欢迎在评论区评论交流。最好的关系是相互成就。您的点赞是建宇创作最大的动力。感谢您的支持。文章持续更新,可以微信搜索【脑补炸鱼】阅读,回复【000】有我准备的一线工厂面试的算法题和资料的解答和资料。本文已收录在GitHubgithub.com/eddycjy/blog,欢迎更新Star。参考InGOprogramminglanguagewhatarethebenefitsofusingArraysoverSlices?为什么在Go中有数组?
