在处理数据集合时,Go 提供了三种主要方式:数组(Array)、切片(Slice)和映射(Map)。其中,切片映射是日常开发中使用频率最高的数据结构。本章将介绍它们的区别与用法。

示例代码

package main

import "fmt"

func main() {
    // 1. 数组 (Array)
    // 长度是类型的一部分,固定不可变
    var arr [3]int = [3]int{10, 20, 30}
    fmt.Println("Array:", arr)

    // 2. 切片 (Slice)
    // 动态数组,引用类型
    slice := []int{1, 2, 3, 4, 5}

    // 操作:切取子集 [start:end] (左闭右开)
    subSlice := slice[1:3] // 包含索引 1, 2 的元素 -> {2, 3}
    fmt.Println("SubSlice:", subSlice)

    // 操作:追加元素
    // 当容量不足时,append 会自动扩容
    slice = append(slice, 6)
    fmt.Println("Appended Slice:", slice)

    // 3. 映射 (Map)
    // 键值对集合,类似 Python 的 dict 或 Java 的 HashMap
    scores := make(map[string]int)
    scores["Alice"] = 95
    scores["Bob"] = 88

    // 检查键是否存在
    // val 是值,ok 是布尔值(存在为 true)
    if score, ok := scores["Alice"]; ok {
        fmt.Printf("Alice's score is %d\n", score)
    }

    // 删除键值对
    delete(scores, "Bob")

    // 遍历 Map (注意:遍历顺序是随机的)
    for name, score := range scores {
        fmt.Printf("%s: %d\n", name, score)
    }
}

关键点解释

数组 (Array)

  • 声明:[Length]Type。例如 [5]int[10]int 是完全不同的类型。

  • 数组是 值类型,赋值或传递给函数时会发生拷贝(复制整个数组)。一般很少直接使用。

切片 (Slice)

  • 声明:[]Type(不指定长度)。

  • 切片本质上是对底层数组的一个“视窗”,包含三个属性:指针、长度 (len)、容量 (cap)。

  • 推荐:使用 make([]Type, len, cap) 创建切片,或使用字面量 []Type{…​}

  • append 函数可能会返回一个新的切片引用(当发生扩容时),所以必须重新赋值:s = append(s, val)

映射 (Map)

  • 声明:map[KeyType]ValueType

  • 必须使用 make 初始化,或者使用字面量。未初始化的 map 是 nil,向其写入会导致 panic。

  • Key 必须是支持比较(==)的类型(如 int, string),切片不能作为 Key。

  • 所有的 Map 操作都不是线程安全的(并发读写需要加锁)。

小结

  • 数组长度固定,切片长度动态。优先使用切片。

  • Map 处理键值对,查找速度快。

  • 切片和 Map 都是引用类型,传递给函数时不会拷贝底层数据,效率高。

练习题

  1. 创建一个包含 10 个元素的整型切片,使用 range 遍历并打印所有偶数。

  2. 统计一段英文文本中每个字符出现的次数,使用 map[rune]int 存储并打印结果。


相关阅读