数组、切片、map

2026年9月6日

一组同类型的值,Go 里常用三种容器:数组长度写死,切片长度可变,map 按 key 存 value。日常写代码几乎都用切片和 map。


数组

数组里的元素必须同一类型,声明时长度是类型的一部分[3]int[4]int 不是同一种类型。

var nums [3]int
nums[0] = 1
nums[1] = 2
fmt.Println(nums) // [1 2 0]
go

也可以在声明时写满:

names := [3]string{"槿", "go", "笔记"}
go

长度用 ... 让编译器数:

names := [...]string{"a", "b", "c"} // [3]string
go

改某个位置只能靠下标。数组是值类型,赋值或传参会整份拷贝

索引

下标从 0 开始,最后一个是 len(a)-1。越界会直接 panic。

a := [3]int{10, 20, 30}
fmt.Println(a[0], a[2]) // 10 30
go

切片

数组长度写死后就不能再长,所以更常用切片(slice)。切片长度可变,底层还是一段数组。

var s []int          // nil 切片
s = append(s, 1, 2)
fmt.Println(s, len(s), cap(s))
go

字面量直接写:

s := []int{1, 2, 3}
go

len 是当前元素个数,cap 是底层数组还能装多少。append 空间不够时会换一块更大的数组。

make 函数

除了基本类型,只声明不赋值的切片、map、channel 是 nilnil 切片可以 append,但读下标会 panic。要用指定长度、容量时用 make

s := make([]int, 3, 5) // len=3, cap=5,三个元素都是 0
s = append(s, 4)
go

make([]T, n) 只写长度时,容量等于长度。

为什么叫切片

因为它常常是从数组(或另一个切片)上「切」出来的:

a := [5]int{0, 1, 2, 3, 4}
s := a[1:4] // [1 2 3],含头不含尾
go

切片和底层数组共享存储。改 s[0]a[1] 也会变。需要独立副本时用 append 到新切片,或 copy

dst := make([]int, len(s))
copy(dst, s)
go

切片排序

整数、浮点、字符串用 slices(Go 1.21+)或旧的 sort

import "slices"

s := []int{3, 1, 2}
slices.Sort(s) // [1 2 3]
go

自定义顺序用 slices.SortFunc


map

map 是无序的键值对。key 必须可比较(整数、字符串、布尔等),value 任意类型。用之前必须初始化,否则赋值会 panic。

var m map[string]int // nil,不能写
m = make(map[string]int)
m["age"] = 18
go

也可以字面量一起写:

m := map[string]int{
	"age": 18,
	"n":   2,
}
go

删 key 用 delete(m, "age")。遍历顺序每次不一定相同。

key 其实不限于「基本类型」,结构体、数组只要可比较也能当 key;切片、map、函数不行。

map 取值

一个返回值时,没有这个 key 就得到 value 的零值,分不清「没有」和「值就是 0」:

age := m["age"]
go

两个返回值时,第二个是「在不在」:

age, ok := m["age"]
if !ok {
	fmt.Println("没有 age")
}
go

参考文档