数组

  • 默认情况下,数组的每个元素都被初始化为元素类型对应的零值
  • 如果在数组的长度位置出现的是“…”省略号,则表示数组的长度是根据初始化值的个数来计算
  • 数组的长度是数组类型的一个组成部分,因此[3]int和[4]int是两种不同的数组类型
  • 定义了一个含有100个元素的数组r,最后一个元素被初始化为-1,其它元素都是用0初始化。 r := [...]int{99: -1}
  • 如果一个数组的元素类型是可以相互比较的,那么数组类型也是可以相互比较的,支持==!=
  • 当调用一个函数的时候,函数的每个调用参数将会被赋值给函数内部的参数变量,所以函数参数变量接收的是一个复制的副本,并不是原始调用的变量。
  • 数组依然很少用作函数参数;相反,我们一般使用slice来替代数组。

Slice

  • 一个slice由三个部分构成:指针、长度和容量
  • 内置的len和cap函数分别返回slice的长度和容量。
  • 多个slice之间可以共享底层的数据,并且引用的数组部分区间可能重叠
  • x[m:n]切片操作对于字符串则生成一个新字符串,如果x是[]byte的话则生成一个新的[]byte。
  • slice将允许在函数内部修改底层数组的元素
  • slice之间不能比较,因此我们不能使用==操作符来判断两个slice是否含有全部相等元素
  • 标准库提供了高度优化的bytes.Equal函数来判断两个字节型slice是否相等([]byte),但是对于其他类型的slice,我们必须自己展开每个元素进行比较

Map

  • 它是一个无序的key/value对的集合
  • 在Go语言中,一个map就是一个哈希表的引用
  • key必须是支持==比较运算符的数据类型
  • 不建议将浮点数作为key值
  • 创建map的几种方法:
1
2
3
4
5
6
7
8
ages := make(map[string]int)

ages = map[string]int{
    "cat": 321,
    "dog": 123
}

ages = map[string]int{}
  • 删除:delete(ages, "cat")
  • map中的元素并不是一个变量,所以不能进行取地址操作&。原因是扩容导致地址失效
  • Map的迭代顺序是不确定的
  • 判断元素是否存在于map中的写法
1
2
3
if cat, ok := ages["cat"]; !ok{
    // do something
}
  • go语言没有实现set
  • 将slice设置为map的key的方法(将slice转字符串)
1
2
3
4
5
6
var m = make(map[string]int)

func k(list []string) string { return fmt.Sprintf("%q", list) }

func Add(list []string)       { m[k(list)]++ }
func Count(list []string) int { return m[k(list)] }

结构体

  • 可以对对成员取地址,然后通过指针访问
  • 结构体成员的顺序有影响(不同顺序但同样成员的结构体不算同一个结构体)
  • 如果结构体没有任何成员的话就是空结构体,写作struct{}。它的大小为0,也不包含任何信息,但是有时候依然是有价值的,如用map实现set
1
2
3
4
5
6
seen := make(map[string]struct{}) // set of strings
// ...
if _, ok := seen[s]; !ok {
    seen[s] = struct{}{}
    // ...first time seeing s...
}
  • 结构体初始化
1
2
type T struct{ a, b int }
var _ = T{a:1, b:2}
  • 如果考虑效率的话,较大的结构体通常会用指针的方式传入和返回
  • 如果要在函数内部修改结构体成员的话,用指针传入是必须的;因为在Go语言中,所有的函数参数都是值拷贝传入的,函数参数将不再是函数调用时的原始变量
  • 如果结构体的全部成员都是可以比较的,那么结构体也是可以比较的
  • 结构体的匿名成员
    • 只声明一个成员对应的数据类型而不指名成员的名字
1
2
3
4
type Circle struct {
    Point
    Radius int
}
  • 得益于匿名嵌入的特性,我们可以直接访问叶子属性而不需要给出完整的路径
1
2
3
4
5
var w Wheel
w.X = 8            // equivalent to w.Circle.Point.X = 8
w.Y = 8            // equivalent to w.Circle.Point.Y = 8
w.Radius = 5       // equivalent to w.Circle.Radius = 5
w.Spokes = 20
  • 不能同时包含两个类型相同的匿名成员

JSON

go标准库提供了go内置类型和json的相互转换功能:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
package main

import (
	"encoding/json"
	"fmt"
)

type Movie struct{
	Title string
	Year int `json:"released"`
	Color bool `json:"color,omitempty"`
	Actor []string
}

func main(){
	var movies = []Movie{
		{
			Title: "Casablanca", 
			Year: 1942 , 
			Color: false, 
			Actor: []string{"Humphrey Bogart", "Ingrid Bergman"}
		},
	}
	//data, err := json.Marshal(movies)
	data, err := json.MarshalIndent(movies, "", "    ")
	if err != nil{
		fmt.Println(err)
	} else {
		fmt.Printf("%s\n", data)
	}
}

其中:

  • json.MarshalIndent(movies, "", " ")将struct转换为带有合适缩进的字符串
  • struct成员必须要大写开头,如果Actor改为actor,那么转换为字符串时将不包含actor这个key
  • 结构体成员Tag(一个结构体成员Tag是和在编译阶段关联到该成员的元信息字符串)转换为字符串后,Year将用release来显示
  • omitempty选项,表示当Go语言结构体成员为零值时不生成该JSON对象(这里false为零值

解码(从json转换为struct)

1
2
3
4
5
var titles []struct{ Title string }
if err := json.Unmarshal(data, &titles); err != nil {
    log.Fatalf("JSON unmarshaling failed: %s", err)
}
fmt.Println(titles) // "[{Casablanca} {Cool Hand Luke} {Bullitt}]"
  • 使用json.Unmarshal()
  • 上述这个例子只包含title,不包含其他属性。

文本和HTML模板