Golang 的内建数据容器

Golang 提供了丰富的内建数据容器,常用的有数组 (Array)、结构体 (Struct)、切片 (Slice)、映射 (Map)、通道 (Channel) 等。

1 数组 (Array)

Golang 提供了数组类型的数据结构。数组是一组已编号且长度固定的数据项序列,它们具有相同唯一的类型。在 Golang 中,数组可以包含任意的原始类型、字符串或者自定义类型。使用示例如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
func main() {
	var arr [10]int  // 声明一个容量为 10 的 int 型数组
	var i int
	for i < 10 {
		arr[i] = i   // 迭代数组并赋值
		i++
	}
	fmt.Println(arr) // 读取数组的内容
}

// 运行结果:  [0 1 2 3 4 5 6 7 8 9]

2 结构体 (Struct)

Golang 支持与 C 语言类似的结构体,在结构体中我们可以为不同字段定义不同的数据类型。结构体定义需要使用 typestruct 语句,示例如下:

1
2
3
4
5
type user_info struct {
    name string
    age int
    description string
}

结构体定义完成后,就可以用它来声明变量:

1
2
3
admin := user_info {"maling", 18, "博主"}
// or
admin := user_info {name: "maling", age: 18, description: "博主"}

3 切片 (Slice)

Golang 切片是对数组的引用,它的长度可以随着程序的运行而动态变化,因而比数组更加的灵活。

3.1 创建切片

创建切片的方法有以下三种:

  1. 通过声明缺省长度的数组来创建切片:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    
    func main() {
        var users []user_info
        // 然后对切片扩容并追加元素
        users = append(users, user_info{"maling", 18, "博主"})
        users = append(users, user_info{"xiaoming", 20, "访客"})
        fmt.Println(users)
    }
    
    // 打印结果:[{maling 18 博主} {xiaoming 20 访客}]
    
  2. 通过内建的 make() 函数来创建切片:

    1
    2
    3
    4
    5
    6
    7
    8
    
    // 创建初始容量为 2,初始元素个数为 0 的切片
    var users = make([]user_info, 0, 2)
    users[0] = user_info{"maling", 18, "博主"}
    users[1] = user_info{"xiaoming", 20, "访客"}
    // users[2] = user_info{"xiaohong", 19, "访客"} 下标越界异常
    
    // 扩容并追加:
    users = append(users, user_info{"xiaohong", 19, "访客"})

3.2 切片初始化

除了迭代赋值,切片还提供了以下几种快捷的初始化方式:

  • 直接初始化:

    1
    
    slice := []int{1, 2, 3}
  • 直接引用已存在的数组

    1
    2
    3
    4
    5
    6
    7
    8
    
    // 引用整个数组
    slice := arr[:]
    // 引用 startIndex 到 endIndex -1 之间的元素
    slice := arr[$startIndex:$endIndex]
    // 引用 startIndex 到末尾之间的元素
    slice := arr[$startIndex:]
    // 引用开始到 endIndex -1 之间的元素
    slice := arr[:$endIndex]
  • 通过切片初始化切片:

    1
    
    slice2 := slice1[$startIndex:$endIndex]

4 映射 (Map)

Golang 的 map 类型类似于 Java 中的 Map<K, V>,是一种无序的键值对集合。map 常用于数据检索,若键不存在,则返回对应类型的零值;否则返回对应键的值。

4.1 创建 Map

创建 Map 的方法有以下三种:

  • 通过内建 make() 函数创建:
    1
    2
    
    map_var := make(map[keyType]valueType, initCapacity)
    // eg: myMap := make(map[string]int, 10)
    
  • 使用字面量创建 Map:
    1
    2
    3
    4
    
    myMap := map[string]int {
        "key1": 1,
        "key2": 2,
    }

4.2 操作 Map

创建好 Map 后,我们还可以对其进行增删改查操作:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
// 插入、修改数据
myMap["key1"] = 1
// 获取数据
fmt.Println(myMap["key1"])
// 获取 map 长度
len(myMap)
// 删除元素
delete(myMap, "key1")
// 遍历 Map
for k, v := range myMap {
    fmt.Println(k, v)
}
// 检查键是否存在(增加 exists 关键字)
value, exists := myMap["key3"]

5 通道 (Channel)

Golang Channel 是 goroutine 之间通信是使用的管道数据结构,其关键字为 chan

chan 提供了一种安全的并发方式,用于在发送端 (sender) 和接收端 (receiver) 之间传递数据。由于涉及到协程 (coroutine) 的概念,因此本文就不详细展开了,这里只介绍一下 chan 容器的基础操作方法。

5.1 创建通道

Golang 可以使用 make() 函数创建通道:

1
2
ch := make(chan datatype)
// eg: ch := make(chan int) // 整数类型的通道

5.2 发送、接收数据

Golang 通过 <- 操作符实现通道数据的收发:

1
2
3
ch <- 10 // 将 10 发送到 ch 通道

value := <- ch // 从管道接收整数,并赋值给 value

5.3 关闭通道

使用 close() 函数可以关闭通道,通道关闭后无法再发送数据,但接收端仍可以继续接收剩余的数据:

1
close(ch)

5.4 select 非阻塞操作

默认情况下,通道发送和接收数据都是阻塞的。通过 select 语句的 default 分支,可以实现对通道的非阻塞操作:

1
2
3
4
5
6
7
8
for true {
    select {
        case <-ch:
            ...
        default: // 无 chan 就绪时,执行 default
            continue // 继续循环,不要阻塞
    }
}