GO语言入门详解(三)之 复合数据类型

一、前言        

        在第二章我们讨论了基本数据类型,它们可以用于构建程序中数据的结构,是Go语言世界的原子。在本章,我们将讨论复合数据类型,它是以不同的方式组合基本类型而构造出来的复合数据类型。我们主要讨论四种类型——数组、slice、map和结构体——同时在本章的最后,我们将演示如何使用结构体来解码和编码到对应JSON格式的数据。   

        数组和结构体是聚合类型;它们的值由许多元素或成员字段的值组成。数组是由同构的元素组成——每个数组元素都是完全相同的类型——结构体则是由异构的元素组成的。数组和结构体都是有固定内存大小的数据结构。相比之下,slice和map则是动态的数据结构,它们将根据需要动态增长。

二、数组 

        数组是一个由固定长度的特定类型元素组成的序列,一个数组可以由零个或多个元素组成。因此在Go语言中很少直接使用数组。和数组对应的类型是Slice(切片),它是可以增长和收缩的动态序列,slice功能也更灵活,但是要理解slice工作原理的话需要先理解数组。 

        数组的每个元素可以通过索引下标来访问,索引下标的范围是从0开始到数组长度减1的位置。内置的len函数将返回数组中元素的个数。 

func main() {
	//创建并初始化一个数组
	var a = [5]int{1, 2, 3, 4, 6}
	fmt.Println(a)
	//遍历数组
	for i := range a {
		fmt.Println(a[i])
	}
	//打印下标 以及对于下标的值
	for i, i2 := range a {
		fmt.Println(i, i2)
	}
}

        在数组字面值中,如果在数组的长度位置出现的是“...”省略号,则表示数组的长度是根据初始化值的个数来计算。因此,上面q数组的定义可以简化为

q := [...]int{1, 2, 3}
fmt.Printf("%T\n", q) // "[3]int"

修改对应索引的值 

r := [...]int{99: -1}

{99: -1} 表示在数组的第 99 个位置上赋值为 -1。这里的 99 是索引,而 -1 是该索引对应的值。由于没有为其他索引提供值,未初始化的索引(0 到 98 和 100 及以上)将默认被初始化为 0

         如果一个数组的元素类型是可以相互比较的,那么数组类型也是可以相互比较的,这时候我们可以直接通过==比较运算符来比较两个数组,只有当两个数组的所有元素都是相等的时候数组才是相等的。不相等比较运算符!=遵循同样的规则。

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

 三、Slice

        Slice(切片)代表变长的序列,序列中每个元素都有相同的类型。一个slice类型一般写作[]T,其中T代表slice中元素的类型;slice的语法和数组很像,只是没有固定长度而已。

        数组与 slice 紧密相关。slice 是轻量级数据结构,可访问数组部分或全部元素,其底层引用数组对象。slice 由指针、长度和容量构成:指针指向首个元素对应底层数组元素地址,首个元素不一定是数组首元素;长度即元素数目,不超容量,容量通常是从 slice 起始到数组末尾。内置 len 和 cap 函数分别返回 slice 的长度和容量。

        多个slice之间可以共享底层的数据,并且引用的数组部分区间可能重叠。图4.1显示了表示一年中每个月份名字的字符串数组,还有重叠引用了该数组的两个slice。数组这样定义

func main() {
	months := [...]string{
		1:  "January",
		2:  "February",
		3:  "March",
		4:  "April",
		5:  "May",
		6:  "June",
		7:  "July",
		8:  "August",
		9:  "September",
		10: "October",
		11: "November",
		12: "December",
	}
	// 打印每个月份
	for i, month := range months {
		if month != "" { // 只打印非空字符串
			fmt.Printf("Month %d: %s\n", i, month)
		}
	}
}

         因此一月份是months[1],十二月份是months[12]。通常,数组的第一个元素从索引0开始,但是月份一般是从1开始的,因此我们声明数组时直接跳过第0个元素,第0个元素会被自动初始化为空字符串。

        如果切片操作超出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]"

        另外,字符串的切片操作和[]byte字节类型切片的切片操作是类似的。都写作x[m:n],并且都是返回一个原始字节序列的子序列,底层都是共享之前的底层数组,因此这种操作都是常量时间复杂度。x[m:n]切片操作对于字符串则生成一个新字符串,如果x是[]byte的话则生成一个新的[]byte。

        因为slice值包含指向第一个slice元素的指针,因此向函数传递slice将允许在函数内部修改底层数组的元

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值