数组、切片与映射

数组:固定长度

数组长度在声明时确定且不可变,实际工程中直接使用数组的场景较少:

var arr [3]int          // [0 0 0]
nums := [3]int{1, 2, 3} // 字面量初始化
// 长度由初始化元素推断
langs := [...]string{"Go", "Python"}

数组是值类型,赋值与传参都会完整拷贝。正因如此,Go 中通常使用切片代替数组。

切片:可动态增长的序列

切片是对底层数组一段连续内存的引用视图,由三个字段构成:指针(指向底层数组)、长度 len、容量 cap

package main

import "fmt"

func main() {
    // 字面量创建
    s := []int{1, 2, 3}

    // make 创建:make([]T, 长度, 容量)
    buf := make([]int, 3, 10)
    fmt.Println(len(buf), cap(buf)) // 3 10

    // 切片操作 s[low:high](左闭右开)
    sub := s[1:3] // [2 3]

    // append 追加元素,容量不足时自动扩容
    s = append(s, 4, 5)
    s = append(s, sub...)

    // copy 复制元素
    dst := make([]int, len(s))
    n := copy(dst, s)

    fmt.Println(s, dst, n)
}

切片的常见操作

s := []int{1, 2, 3, 4, 5}
s[1:3]   // [2 3]:下标 1 到 3 之前
s[:2]    // [1 2]:省略 low,从头开始
s[3:]    // [4 5]:省略 high,到末尾
s = s[:len(s)-1] // 删除末尾元素
Warning

切片共享底层数组。sub := s[1:3] 得到的切片修改元素会影响 sappend 在容量足够时也会写入原数组。若需要独立副本,使用 copy 或 Go 1.21+ 的 slices.Clone

映射:键值对集合

package main

import "fmt"

func main() {
    // 字面量创建
    ages := map[string]int{
        "Alice": 30,
        "Bob":   25,
    }

    // make 创建
    scores := make(map[string]int)

    // 增、改、查、删
    scores["math"] = 90
    v := scores["math"]
    delete(ages, "Bob")

    // 判断键是否存在(重要惯用法)
    if val, ok := ages["Alice"]; ok {
        fmt.Println("Alice:", val)
    } else {
        fmt.Println("键不存在,零值:", val)
    }

    _ = v
    fmt.Println(len(scores)) // 元素个数
}

遍历与有序性

映射的遍历顺序是随机的,这是语言层面的刻意设计。若需要稳定顺序,须对键排序后遍历:

keys := make([]string, 0, len(ages))
for k := range ages {
    keys = append(keys, k)
}
sort.Strings(keys)
for _, k := range keys {
    fmt.Println(k, ages[k])
}
Warning

映射元素不可寻址(无法对 m["k"] 取地址)。映射本身不是并发安全的,并发读写需要加锁或使用 sync.Map,详见并发编程章节。

range 遍历

range 支持切片、数组、映射、字符串与通道:

表达式第一返回值第二返回值
range slice下标元素值
range map
range string字节下标rune 字符
range channel元素值-

slices 与 maps 工具包(Go 1.21+)

标准库新增了泛型工具包,覆盖常用的查找、排序、去重等操作:

import (
    "slices"
    "maps"
)

nums := []int{3, 1, 2}
slices.Sort(nums)                       // 排序
idx, found := slices.BinarySearch(nums, 2) // 二分查找
cloned := slices.Clone(nums)            // 克隆
merged := maps.Clone(ages)              // 克隆映射
_ = idx
_ = found
_ = merged

小结

  • 切片是工程实践中的首选序列类型,注意共享底层数组带来的副作用。
  • 映射遍历无序,"comma ok" 惯用法用于判断键是否存在。
  • Go 1.21+ 优先使用 slicesmaps 包而非手写工具函数。