内容简介:数组是一个由默认情况下,数组的每个元素都被初始化为元素类型对应的零值,对于数字类型来说就是0。在数组字面值中,如果在数组的
数组
数组是一个由 固定长度
的特定类型元素组成的序列,一个数组可以由零个或多个元素组成。因为数组的长度是固定的,因此在 Go 语言中很少直接使用数组。和数组对应的类型是Slice(切片),它是可以增长和收缩动态序列。
默认情况下,数组的每个元素都被初始化为元素类型对应的零值,对于数字类型来说就是0。
var a [3]int var q [3]int = [3]int{1,2,3} var r [3]int = [3]int{1,2} fmt.Println(r[2]) // 0
在数组字面值中,如果在数组的 长度位置
出现的是“...”省略号,则表示数组的长度是根据 初始化值的个数
来计算。因此,上面q数组的定义可以简化为:
var q := [...]int{1,2,3} fmt.Printf("%T\n", q) // [3]int
数组的 长度
是数组类型的一个组成部分,因此[3]int和[4]int是两种不同的数组类型。数组的长度必须是 常量表达式
,因为数组的长度需要在 编译阶段
确定。
q := [3]int{1,2,3} q = [3]int{1,2,3,4} // cannot use [4]int literal (type [4]int) as type [3]int in assignment
定义了一个含有100个元素的数组r,最后一个元素被初始化为-1,其它元素都是用0初始化:
r := [...]int{99:-1}
如果一个数组的元素类型是可以相互比较的,那么数组类型也是可以相互比较的,这时候我们可以直接通过==比较运算符来比较两个数组,只有当两个数组的所有元素都是相等的时候数组才是相等的。不相等比较运算符!=遵循同样的规则。
a := [2]int{1, 2} b := [...]int{1, 2} c := [2]int{1, 3} fmt.Println(a == b, a == c, b == c) // "true false false" d := [3]int{1, 2} fmt.Println(a == d) // compile error: cannot compare [2]int == [3]int
如果我们要使用函数对数组进行操作,我们传入的应该是指针,而不是数组。这是因为,当函数 参数变量
接收的是一个 复制的副本
,并不是原始调用的变量。因为函数参数传递的机制导致传递 大的数组类型将是低效的
,并且对数组参数的任何的修改都是发生在复制的数组上,并不能直接修改调用时原始的数组变量。
下面的函数用于给[32]byte类型的数组清零:
func zero(ptr *[32]byte) { for i := range ptr { ptr[i] = 0 } }
虽然通过指针来传递数组参数是高效的,而且也允许在函数内部修改数组的值,但是数组依然是 僵化的类型
,因为数组的类型包含了僵化的长度信息。上面的zero函数并不能接收指向[16]byte类型数组的指针,而且也没有任何添加或删除数组元素的方法。由于这些原因,除了像SHA256这类需要处理特定大小数组的特例外, 数组依然很少用作函数参数
;相反,我们一般使用slice来替代数组。
Slice
Slice(切片)代表变长的序列,序列中每个元素都有相同的类型。一个slice类型一般写作[]T,其中T代表slice中元素的类型;slice的语法和数组很像,只是 没有固定长度
而已。
数组和slice之间有着紧密的联系。一个slice是一个 轻量级的数据结构
,提供了访问数组子序列(或者全部)元素的功能,而且slice的底层确实 引用一个数组对象
。一个slice由三个部分构成: 指针、长度和容量
。指针指向第一个slice元素对应的底层数组元素的地址,要注意的是slice的第一个元素并不一定就是数组的第一个元素。 长度对应slice中元素的数目
;长度不能超过容量, 容量一般是从slice的开始位置到底层数据的结尾位置
。内置的len和cap函数分别返回slice的长度和容量。
slice的切片操作s[i:j],其中0 ≤ i≤ j≤ cap(s),用于创建一个新的slice,引用s的从第i个元素开始到第j-1个元素的子序列。新的slice将只有j-i个元素。如果i位置的索引被省略的话将使用0代替,如果j位置的索引被省略的话将使用len(s)代替。
下面代码和图片更直观的解释了slice:
months := [...]string{1: "January", /* ... */, 12: "December"} Q2 := months[4:7] summer := months[6:9] fmt.PrintIn(Q2) // ["April" "May" "June"] fmt.PrintIn(summer) // ["June" "July" "August"]
如果slice超出了容量cap(s)的上限将导致一个panic异常,但是超出len(s)则是意味着扩展了slice,因为新slice的长度会变大:
fmt.Println(summer[:20]) // panic: out of range endlessSummer := summer[:5] // extend a slice (within capacity) fmt.Println(endlessSummer) // "[June July August September October]"
和数组不同的是, slice之间不能比较
,因此我们不能使用==操作符来判断两个slice是否含有全部相等元素。不过标准库提供了高度优化的bytes.Equal函数来判断两个字节型slice是否相等([]byte),但是对于其他类型的slice,我们必须自己展开每个元素进行比较:
func equal(x, y []string) bool { if len(x) != len(y) { return false } for i := range x { if x[i] != y[i] { return false } } return true }
为何slice不直接支持比较运算符呢?这方面有两个原因:
- 一个slice的元素是间接引用的,一个slice甚至可以包含自身。
- 因为slice的元素是间接引用的,一个固定的slice值(指slice本身的值,不是元素的值)在不同的时刻可能包含不同的元素,因为底层数组的元素可能会被修改。
关于slice更多的信息,下次再详细的写吧。
参考
《GO语言圣经》
以上所述就是小编给大家介绍的《GO的第五天,复合数据类型---数组、Slice》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- MongoDB指南---10、索引、复合索引 简介
- 为什么要有复合索引?
- python – Django或类似的复合主键
- 复合设计模式(Composite Design Pattern)
- Redis进阶应用:Redis+Lua脚本实现复合操作
- ICLR2020 | 谷歌最新研究:用“复合散度”量化模型合成泛化能力
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Markdown 在线编辑器
Markdown 在线编辑器
HEX CMYK 转换工具
HEX CMYK 互转工具