Go语言中有四种复合数据类型:数组,slice,map,结构体
数组和结构体都是聚合类型,长度固定。而slice和map都是动态数据结构,长度可变。
- 数组
var arrName [len]T
arrName := [...]int{1, 2, 3}
arrName是数组的名字,len是数组的长度,需要时常量,在编译时确定。T是数组元素的类型。
长度也可以通过初始化元素个数决定,这时,len部分使用"..."代替。
如果数组元素是可比较的,那么数组也是可比较的。但只能是同类型的数组才能比较,否则编译错误。
%t - 输出一个bool值,%T - 输出一个变量的类型。
****** 数组是按值传递的,但可以通过传递一个数组指针。
2.slice
表示一个拥有相同类型元素的可变长度序列:[]T,看上去像没有长度的数组类型。
slice有三个属性:指针,长度,容量, len, cap
一个底层数组可以对应多个slice,而且这些slice之间也可以重叠。
可以通过子串生成操作,从数组或者slice得到slice,访问容量外的元素,会导致宕机错误。
创建一个数组的slice,等于为数组创建了一个别名。
和数组不同,slice无法做比较,因此不能用 == 在测试两个slice是否拥有相同的元素。标准库里面提供了高度优化的bytes.Equal来比较两个字节slice ([]byte),但对于其他类型,则需要我们自己写函数来比较。
slice只能与nil做比较,slice不能作为map的key。
对于可能修改slice属性的函数,一般都返回 修改后的slice,规则是:将函数返回值赋值给此前的slice。 xs = append(xs, y)
copy函数也很常用。
slice并不是纯引用类型,而是像下面这种聚合类型:
type IntSlice struct {
ptr *int
len, cap int
}
对于下面的列子:
func modifySlice(s []int) {
s[0] = 0
}
func appendSlice(s []int) {
s = append(s, 4)
}
func main() {
s := []int { 1, 2, 3 }
fmt.Println(s) // [1,2,3]
modifySlice(s) // [0,2,3], 虽然ptr是传值的,但其是指针,可以修改其所指地址的内容,当然这里要在len的范围内。
appendSlice(s) // [0,2,3], ptr,len, cap都是传值的,虽然在函数里面len发生了变化,ptr, cap是否发生变化不定,但当其返回这里时,ptr, len, cap都不可能变化,因此s没有变化
}
注意:Go语言中的参数,都是按值传递。
- map
GO语言中的map是散列表的引用,map[K]V,其中K类型,必须可以通过 == 进行比较,虽然浮点数也可以,但不是一个好主意。
可以用make来创建一个map:ages := make( map[string]int ) 或者
ages := map[string]int {
"alice":31,
"charlie":34,
}
可以使用delete函数从map中删除一个元素。 delete(ages, "alice")
但是map元素不是一个变量,不可以获取它的地址。因为map是动态变化的。
对于nil的map,可以查找,删除,获取元素个数,但是设置操作会导致错误。设置元素前,必须初始化map。
和slice一样,map也是不可比较的,唯一合法的就是和nil做比较。
map可以实现集合类型,map[T]bool
unicode.ReplacementChar 就是解析Unicode编码遇到不合法的UTF-8字符时,返回的值。
map值类型本身可以是复合类型:map[string]map[string]bool
4.结构体
type Name struct {
IDint
Namestring
ManagerIDint
}
可以通过点号访问起成员,点号也适用于结构体指针,和C语言不一样,不需要 ->
结构体定义中不可以包含自己,但可以包含本身的指针,这样可以构造链表,树等结构。
没有任何成员的结构体称为空结构体,写作 struct {}。他没有长度,也不携带任何信息。
其成员是否可以导出,看其名字的首字母是否大写。
在Go这种按值调用的语言中,调用函数接收到的是实参的一个副本,并不是实参的引用。
Go允许我们定义不带名称的结构体成员,只需要制定类型即可;这种结构体成员称为匿名成员。这个结构体成员的类型必须是一个命名类型或者指向命名类型的指针。
type Point struct {
X, Y int
}
type Circle struct {
Point
Radius int
}
var circle Circle
circle.X = 5
以快捷方式访问匿名成员的内部变量,同样适用于访问匿名成员的内部方法。因此,外围的结构体类型获取的不仅是匿名成员的内部变量,还有相关的方法。这个机制就是从简单类型对象组合成复合类型对象的主要方式。 - 在Go中,组合是面向对象编程方式的核心。
5.JSON
JavaScript对象表示法(JSON)是一种发送和接收格式化信息的标准。其它标准还有:XML,ASN.1,Google的ProtocolBuffer。
JSON基本类型:数字、布尔值、字符串。
基础类型可以通过JSON的数组和对象进行组合。
JSON数组是一个有序的元素序列,每个元素之间使用逗号分割,两边使用方括号括起来。 - 数组,slice
JSON对象是一个从字符串到值的映射,写成name:value对的序列,每个元素之间用逗号分割,两边使用花括号括起来。 - map,结构体
例子:
booleantrue
number-234.23
string"She said "Hello, 世界""
array[“gold”, "silver", "bronze"]
object{ "year":1980,
"event":"archery"
"medals":[“gold”, "silver", "bronze"]
}
json包的Marshal和UnMarshal对数据进行json封包和解包。
而MarshalIndent可以格式化结果成可读的形式。包括前缀字符串和缩进字符串。
Marshal使用Go结构体成员名称作为JSON对象里面字段的名称,只有可导出的成员可以转换为JSON字段。
标签定义:由一串由空格分开的标签键值对key:"value"组成的。value在双引号内,因此是原生的字符串字面量。
json键控制encoding/json的行为。
例子:
type Movie struct {
Titlestring
Yearintjson:"released"
Colorbooljson:"color,omitempty"
Actors[]string
}
Year在json对象中的名称是released
Color在json对象中的名称是color,且如果其值为零或者为空,则不输出到JSON对象中。