- ubuntu12.04环境下使用kvm ioctl接口实现最简单的虚拟机
- Ubuntu 通过无线网络安装Ubuntu Server启动系统后连接无线网络的方法
- 在Ubuntu上搭建网桥的方法
- ubuntu 虚拟机上网方式及相关配置详解
CFSDN坚持开源创造价值,我们致力于搭建一个资源共享平台,让每一个IT人在这里找到属于你的精彩世界.
这篇CFSDN的博客文章浅谈Go数组比切片好在哪由作者收集整理,如果你对这篇文章有兴趣,记得点赞哟.
前段时间有播放一条快讯,就是 Go1.17 会正式支持切片(Slice)转换到数据(Array),不再需要用以前那种骚办法了,安全了许多.
但是也有同学提出了新的疑惑,在 Go 语言中,数组其实是用的相对较少的,甚至会有同学认为在 Go 里可以把数组给去掉.
数组相较切片到底有什么优势,我们又应该在什么场景下使用呢?
这是一个我们需要深究的问题,因此今天就跟大家一起来一探究竟,本文会先简单介绍数组和切片是什么,再进一步对数组的使用场景剖析.
一起愉快地开始吸鱼之路.
Go 语言中有一种基本数据类型,叫数组。其格式为:[n]T。是一个包含 N 个类型 T 的值的数组.
基本声明格式为:
1
|
var a [10]int
|
代表的是声明了一个变量 a 是一个包含 10 个整数的数组。数组的长度是其类型的一部分,所以数组不能被随意调整大小.
在使用例子上:
1
2
3
4
5
6
7
8
9
10
|
func main() {
var a [2]string
a[0] = "脑子进"
a[1] = "煎鱼了"
fmt.Println(a[0], a[1])
fmt.Println(a)
primes := [6]int{2, 3, 5, 7, 11, 13}
fmt.Println(primes)
}
|
输出结果:
脑子进 煎鱼了 [脑子进 煎鱼了] [2 3 5 7 11 13] 。
在赋值和访问上,数组可以针对不同的索引,进行单独操作。在内存布局上,数组的索引 0 和 1...是会在相邻区域,可直接访问.
为什么数组在业务代码似乎用的很少。因为 Go 语言有一个切片的数据类型:
基本声明格式为:
1
|
var a []T
|
代表的是变量 a 是带有类型元素的切片T。通过指定两个索引(下限和上限)并用冒号隔开来形成切片:
1
|
a[low : high]
|
在使用例子上:
1
2
3
4
5
6
|
func main() {
primes := [3]string{"煎鱼", "搞", "Go"}
var s []string = primes[1:3]
fmt.Println(s)
}
|
输出结果:
[搞 Go] 。
切片支持动态的扩缩容,不需要用户侧去关注,非常便利。更重要的一点是,切片的底层数据结构中本身就包含了数组:
1
2
3
4
5
|
type slice struct {
array unsafe.Pointer
len int
cap int
}
|
也就很多人笑称:在 Go 语言中数组已经可以下岗了,用切片就完事了... 。
你怎么看待这个说法的呢,快速思考你心中的答案.
在风尘仆仆介绍完数组和切片的基本场景后,在数组的优势方面,先了解一下官方的自述:
Arrays are useful when planning the detailed layout of memory and sometimes can help avoid allocation, but primarily they are a building block for slices. 。
非常粗暴间接:在规划内存的详细布局时,数组是很有用的,有时可以帮助避免分配,但主要是它们是分片的构建块.
我们再进一步解读,看看官方这股 “密文” 具体指的是什么,我们将该密文解读为以下内容进行讲解:
数组是固定长度的,它们之间是可以进行比较的,数组是值对象(不是引用或指针类型),你不会遇到 interface 等比较的误判:
1
2
3
4
5
6
7
|
func main() {
a1 := [3]string{"脑子", "进", "煎鱼了"}
a2 := [3]string{"煎鱼", "进", "脑子了"}
a3 := [3]string{"脑子", "进", "煎鱼了"}
fmt.Println(a1 == a2, a1 == a3)
}
|
输出结果:
false true 。
另一方面,切片不可以直接比较,也不能用于判断:
1
2
3
4
5
6
7
|
func main() {
a1 := []string{"脑子", "进", "煎鱼了"}
a2 := []string{"煎鱼", "进", "脑子了"}
a3 := []string{"脑子", "进", "煎鱼了"}
fmt.Println(a1 == a2, a1 == a3)
}
|
输出结果:
# command-line-arguments ./main.go:10:17: invalid operation: a1 == a2 (slice can only be compared to nil) ./main.go:10:27: invalid operation: a1 == a3 (slice can only be compared to nil) 。
同时数组可以作为 map 的 k(键),而切片不行,切片并没有实现平等运算符(equality operator),需要考虑的问题有非常多,例如:
平等是为结构体和数组定义的,所以这类类型可以作为 map 键使用。切片没有平等的定义,有着非常根本的差距.
数组的可比较和平等,切片做不到.
数组可以提供更高的编译时安全,可以在编译时检查索引范围。如下:
1
2
3
4
5
|
s := make([]int, 3)
s[3] = 3 // "Only" a runtime panic: runtime error: index out of range
a := [3]int{}
a[3] = 3 // Compile-time error: invalid array index 3 (out of bounds for 3-element array)
|
这个编译检查的帮助虽 “小”,但其实非常有意义。我是日常看到各大切片越界的告警,感觉都能背下来了... 。
万一这个越界是在 hot path 上,影响大量用户,分分钟背个事故,再来个 3.25,岂不梦中惊醒?
数组的编译安全,切片做不到.
数组的长度是数组类型声明的一部分,因此长度不同的数组是不同的类型,两个就不是一个 “东西”.
当然,这是一把双刃剑。其优势在于:可用于显式指定所需数组的长度.
例如:你在业务代码中想编写一个使用 IPv4 地址的函数。可以声明 type [4]byte。使用数组有以下意识:
同时数组的长度,也可以用做记录目的:
在特定业务场景上,使用数组更好.
数组可以更好地控制内存布局,因为不能直接在带有切片的结构中分配空间,所以可以使用数组来解决.
例如:
1
2
3
|
type Foo struct {
buf [64]byte
}
|
不知道你是否有在一些 Go 图形库上见过这种不明所以的操作,例子如下:
1
2
3
4
5
6
7
8
|
type TGIHeader struct {
_ uint16 // Reserved
_ uint16 // Reserved
Width uint32
Height uint32
_ [15]uint32 // 15 "don't care" dwords
SaveTime int64
}
|
因为业务需求,我们需要实现一个格式,其中格式是 "TGI"(理论上的Go Image),头包含这样的字段:
这么一看,也就不难理解数组的在这个场景下的优势了。定长,可控的内存,在计划内存布局时非常有用.
使用数组时,其访问(单个)数组元素比访问切片元素更高效,时间复杂度是 O(1)。例如:
1
2
3
4
|
var a [2]string
a[0] = "脑子进"
a[1] = "煎鱼了"
fmt.Println(a[0], a[1])
|
切片就没那么方便了,访问某个位置上的索引值,需要:
1
2
|
var a []int{0, 1, 2, 3, 4, 5}
number := numbers[1:3]
|
相对复杂些的,删除指定索引位上的值,可能还有小伙伴纠结半天,甚至在找第三方开源库想快速实现.
无论在访问速度和开发效率上,数组都占一定的优势,这是切片所无法直接对比的.
经过一轮的探讨,我们对 Go 语言的数组有了更深入的理解。总结如下:
In GO programming language what are the benefits of using Arrays over Slices? Why have arrays in Go?
到此这篇关于浅谈Go数组比切片好在哪的文章就介绍到这了,更多相关Go数组切片内容请搜索我以前的文章或继续浏览下面的相关文章希望大家以后多多支持我! 。
原文链接:https://segmentfault.com/a/1190000040700545 。
最后此篇关于浅谈Go数组比切片好在哪的文章就讲到这里了,如果你想了解更多关于浅谈Go数组比切片好在哪的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
服务架构进化论 原始分布式时代 一直以来,我可能和大多数的人认知一样,认为我们的服务架构的源头是单体架构,其实不然,早在单体系
序列化和反序列化相信大家都经常听到,也都会用, 然而有些人可能不知道:.net为什么要有这个东西以及.net frameword如何为我们实现这样的机制, 在这里我也是简单谈谈我对序列化和反序列化的
内容,是网站的核心所在。要打造一个受用户和搜索引擎关注的网站,就必须从网站本身的内容抓起。在时下这个网络信息高速发展的时代,许多低质量的信息也在不断地充斥着整个网络,而搜索引擎对一些高质量的内容
从第一台计算机问世到现在计算机硬件技术已经有了很大的发展。不管是现在个人使用的PC还是公司使用的服务器。双核,四核,八核的CPU已经非常常见。这样我们可以将我们程序分摊到多个计算机CPU中去计算,在
基本概念: 浅拷贝:指对象的字段被拷贝,而字段引用的对象不会被拷贝,拷贝对象和原对象仅仅是引用名称有所不同,但是它们共用一份实体。对任何一个对象的改变,都会影响到另外一个对象。大部分的引用类型,实
.NET将原来独立的API和SDK合并到一个框架中,这对于程序开发人员非常有利。它将CryptoAPI改编进.NET的System.Security.Cryptography名字空间,使密码服务摆脱
文件与文件流的区别(自己的话): 在软件开发过程中,我们常常把文件的 “读写操作” ,与 “创造、移动、复制、删除操作” 区分开来
1. 前言 单元测试一直都是"好处大家都知道很多,但是因为种种原因没有实施起来"的一个老大难问题。具体是否应该落地单元测试,以及落地的程度, 每个项目都有自己的情况。 本篇为
事件处理 1、事件源:任何一个HTML元素(节点),body、div、button 2、事件:你的操作 &
1、什么是反射? 反射 (Reflection) 是 Java 的特征之一,它允许运行中的 Java 程序获取自身的信息,并且可以操作类或对象的内部属性。 Oracle 官方对
1、源码展示 ? 1
Java 通过JDBC获得连接以后,得到一个Connection 对象,可以从这个对象获得有关数据库管理系统的各种信息,包括数据库中的各个表,表中的各个列,数据类型,触发器,存储过程等各方面的信息。
可能大家谈到反射面部肌肉都开始抽搐了吧!因为在托管语言里面,最臭名昭著的就是反射!它的性能实在是太低了,甚至在很多时候让我们无法忍受。不过不用那么纠结了,老陈今天就来分享一下如何来优化反射!&nbs
1. 前言 最近一段时间一直在研究windows 驱动开发,简单聊聊。 对比 linux,windows 驱动无论是市面上的书籍,视频还是社区,博文以及号主,写的人很少,导
问题:ifndef/define/endif”主要目的是防止头文件的重复包含和编译 ========================================================
不知不觉.Net Core已经推出到3.1了,大多数以.Net为技术栈的公司也开始逐步的切换到了Core,从业也快3年多了,一直坚持着.不管环境
以前面试的时候经常会碰到这样的问题.,叫你写一下ArrayList.LinkedList.Vector三者之间的区别与联系:原先一直搞不明白,不知道这三者之间到底有什么区别?哎,惭愧,基础太差啊,木
目录 @RequestParam(required = true)的误区 先说结论 参数总结 @RequestParam(r
目录 FTP、FTPS 与 SFTP 简介 FTP FTPS SFTP FTP 软件的主动模式和被动模式的区别
1、Visitor Pattern 访问者模式是一种行为模式,允许任意的分离的访问者能够在管理者控制下访问所管理的元素。访问者不能改变对象的定义(但这并不是强制性的,你可以约定为允许改变)。对管
我是一名优秀的程序员,十分优秀!