1. 数组
1.1 数组声明与初始化
Go 数组是固定长度、同类型元素的序列。长度是数组类型的一部分, [3]int 和 [5]int 是不同类型,不能互相赋值,赋值后会编译报错。
go
package main
import "fmt"
func main() {
// 声明数组:var 名称 [长度]类型 --- 元素自动为零值
var numbers [5]int // 5个int,全部为0
// 数组字面量初始化:[长度]类型{值1, 值2, ...}
colors := [3]string{"red", "green", "blue"}
// ... 让 Go 自动计算长度
days := [...]string{"Monday", "Tuesday", "Wednesday", "Thursday", "Friday"}
// 通过索引访问和修改元素
numbers[2] = 42 // 修改第3个元素(索引2)
// len() 获取数组长度
fmt.Println("Length of colors array:", len(colors))
// 打印整个数组
fmt.Println("Numbers:", numbers)
fmt.Println("Colors:", colors)
fmt.Println("Days:", days)
}
1.2 数组遍历
- 使用for循环遍历
go
package main
import "fmt"
func main() {
scores := [5]int{85, 92, 78, 96, 88}
// 传统 for 循环:通过索引访问每个元素, Go 的 len() 是编译时确定的(数组长度固定),不需要运行时计算
for i := 0; i < len(scores); i++ {
fmt.Printf("Score %d: %d\n", i+1, scores[i])
}
- 使用range遍历
go
package main
import "fmt"
func main() {
names := [...]string{"Alice", "Bob", "Charlie", "Diana", "Eve"}
// range 同时返回索引和值
fmt.Println("Names with indices using range:")
for index, value := range names {
fmt.Printf("Index %d: %s\n", index, value)
// value 是元素的拷贝,修改 value 不影响原数组
}
}
1.3 多维数组声明与初始化
多维数组是"数组的数组",Go 通过 行列 语法定义:
go
// 初始化 2x4 数组(2行4列)
grades := [2][4]int{{85, 92, 78, 96}, {88, 91, 84, 87}}
1.4 多维数组遍历
- 使用for循环遍历
less
func main() {
// 声明 3x3 矩阵,元素全部为零值 0
var matrix [3][3]int
// 初始化 2x4 数组(2行4列)
grades := [2][4]int{{85, 92, 78, 96}, {88, 91, 84, 87}}
// 修改元素:matrix[行][列]
matrix[1][1] = 5 // 第2行第2列设为5
// 打印矩阵:嵌套 for 循环
fmt.Println("Matrix:")
for i := 0; i < len(matrix); i++ {
for j := 0; j < len(matrix[i]); j++ {
fmt.Printf("%3d ", matrix[i][j]) // %3d 右对齐,宽度3
}
fmt.Println() // 每行末尾换行
}
}
- 使用range遍历
go
package main
import "fmt"
func main() {
grades := [2][4]int{{85, 92, 78, 96}, {88, 91, 84, 87}}
// range 遍历:外层遍历行,内层遍历每行的元素
for i, row := range grades {
for _, grade := range row {
fmt.Printf("%d ", grade)
}
fmt.Println()
}
// 求所有元素总和:嵌套 for 循环
var totalSum int
for i := 0; i < len(grades); i++ {
for j := 0; j < len(grades[i]); j++ {
totalSum += grades[i][j]
}
}
fmt.Println("Total sum of grades:", totalSum)
// 获取维度:行数 × 列数
rows := len(grades)
var cols int
if rows > 0 {
cols = len(grades[0])
}
fmt.Printf("Grades array dimensions: %dx%d\n", rows, cols)
}
1.5 数组排序 --- sort 包
Go 的 sort 包提供排序函数,但只能对切片 排序,需要用 [:] 把数组转为切片。
1.5.1 升序排列
sort.Ints--- int 升序排序
sort.Strings--- string 按字典序
sort.Float64s--- float64 升序
go
package main
import (
"fmt"
"sort"
)
func main() {
numbers := [5]int{64, 34, 25, 12, 22}
names := [4]string{"Charlie", "Alice", "Bob", "David"}
prices := [6]float64{19.99, 9.99, 29.99, 4.99, 39.99, 14.99}
fmt.Println("Before sorting:")
fmt.Println("Numbers:", numbers)
fmt.Println("Names:", names)
fmt.Println("Prices:", prices)
// sort.Ints() 排序 int 切片(升序)
sort.Ints(numbers[:]) // numbers[:] 把数组转为切片
// sort.Strings() 排序 string 切片(升序)
sort.Strings(names[:])
// sort.Float64s() 排序 float64 切片(升序)
sort.Float64s(prices[:])
fmt.Println("\nAfter sorting:")
fmt.Println("Numbers:", numbers)
fmt.Println("Names:", names)
fmt.Println("Prices:", prices)
// 检查是否已排序
isSorted := sort.IntsAreSorted(numbers[:])
fmt.Println("Numbers array is sorted:", isSorted)
运行结果:
yaml
Before sorting:
Numbers: [64 34 25 12 22]
Names: [Charlie Alice Bob David]
Prices: [19.99 9.99 29.99 4.99 39.99 14.99]
After sorting:
Numbers: [12 22 25 34 64]
Names: [Alice Bob Charlie David]
Prices: [4.99 9.99 14.99 19.99 29.99 39.99]
Numbers array is sorted: true
sort.Ints(numbers[:])---numbers[:]把[5]int数组转为[]int切片(切片引用数组的底层内存)
- 排序后数组本身也被修改了!因为切片和数组共享同一块内存
sort.IntsAreSorted(numbers[:])--- 检查切片是否已排序,返回 bool
- 数组不能直接排序:
sort.Ints(numbers)编译报错,必须转为切片
1.5.2 降序排列 sort.Reverse(...)
go
package main
import (
"fmt"
"sort"
)
func main() {
descNumbers := [5]int{1, 2, 3, 4, 5}
fmt.Println("Before reverse sort:", descNumbers)
// 降序排序:sort.Sort + sort.Reverse + sort.IntSlice
sort.Sort(sort.Reverse(sort.IntSlice(descNumbers[:])))
fmt.Println("After reverse sort:", descNumbers)
}
sort.Sort接收一个sort.Interface,对其执行执行排序算法,原地修改底层数组
sort.Reverse(sort.IntSlice(slice))返回一个接口值,内部包装了sort.IntSlice。
它的Less(i, j)实际调用的是IntSlice.Less(j, i),即slice[j] < slice[i],比较方向反转,变成 降序。
sort.IntSlice是一个定义类型。sort.IntSlice(slice)是把[]int类型转换 成sort.IntSlice。不是复制,底层数组仍然是原来的slice。此时它的Less是升序比较。
1.5.3 数组查找 sort.SearchInts(sortedNumbers:, xxx)
go
package main
import (
"fmt"
"sort"
)
func main() {
sortedNumbers := [8]int{5, 12, 23, 34, 45, 67, 78, 89}
binaryTarget := 45
// sort.SearchInts 在已排序切片中查找目标
binaryIndex := sort.SearchInts(sortedNumbers[:], binaryTarget)
// 必须验证:SearchInts 返回的可能是"应插入的位置",而非找到的位置
if binaryIndex < len(sortedNumbers) && sortedNumbers[binaryIndex] == binaryTarget {
fmt.Printf("Binary search: Found %d at index %d\n", binaryTarget, binaryIndex)
} else {
fmt.Printf("Binary search: %d not found\n", binaryTarget)
}
}
2. 切片
2.1 切片创建
切片是 Go 中最核心的数据结构,它是动态数组的"视图",由指针(指向底层数组)、长度len、容量cap三部分组成。
go
package main
import "fmt"
func main() {
// 从数组创建切片:arr[low:high],包含 low 到 high-1 的元素
arr := [5]int{1, 2, 3, 4, 5}
slice1 := arr[1:4] // [2, 3, 4]
// make() 创建切片:make([]type, length, capacity)
slice2 := make([]string, 3, 5) // 长度3,容量5,元素为string零值""
// 长度3:已有3个""元素可访问;容量5:底层数组可容纳5个元素
// 切片字面量:直接声明(与数组字面量不同,没有长度)
slice3 := []int{10, 20, 30} // 切片,不是数组!没有 [N]
// nil 切片:var 声明未初始化
var slice4 []int // slice4 == nil,len=0, cap=0
if slice4 == nil {
fmt.Println("slice4 is nil")
}
// len() 和 cap() 获取长度和容量
length := len(slice2)
capacity := cap(slice2)
// 修改切片元素会影响原数组(共享底层内存)
slice1[1] = 99 // arr[2] 也变成 99
fmt.Println("Original array:", arr)
fmt.Println("slice1:", slice1)
fmt.Println("slice2:", slice2)
fmt.Println("slice3:", slice3)
fmt.Printf("slice2 - length: %d, capacity: %d\n", length, capacity)
}
2.2 切片操作
- 子切片会共享其"父切片"的底层数组;
- 切片为 nil 时,获取子切片的结果仍然是 nil
2.2.1 切片表达式
- 基础形式 :
s[low:high],左闭右开,取索引[low, high)区间的元素。省略 low 默认 0,省略 high 默认原切片长度。
- 区间范围:
0 <= low <= high <= len(s)
go
s := []int{2, 3, 5, 7, 11} // len: 5 cap: 5
s1 := s[1:3]
fmt.Println(s1) // [3 5]
fmt.Println(s[2:]) // [5 7 11] --- 等同于 s[2:len(s)]
fmt.Println(s[:3]) // [2 3 5] --- 等同于 s[0:3]
fmt.Println(s[:]) // [2 3 5 7 11] --- 等同于 s[0:len(s)]
// 索引 low 和 high 必须满足范围 否则会报错
star := "Polaris"
fmt.Println(star[-1]) // 无效参数:索引 -1
fmt.Println(star[0:8]) // 运行时错误:slice bounds out of range [:8] with length 7
// 当从切片获取子切片时,结果切片的容量 cap 等于基础切片的容量减去 low(即 cap(s) - low),而不是基础切片的长度
teams := []string{"Bayern Munich", "Real Madrid", "Manchester City"}
t := teams[1:2]
fmt.Println(t, "| len:", len(t), "cap:", cap(t)) // [Real Madrid] | len: 1 cap: 2
fmt.Println(t[1:3]) // 运行时错误:slice bounds out of range [:3] with capacity 2
// 简单切片表达式支持数组、切片、字符串,以及指向数组/切片的指针.
consts := new([3]float64)
*consts = [3]float64{3.1416, 2.718, 1.618} // 指向数组
fmt.Println(consts[1:3]) // [2.718 1.618]
animals := new([]string)
*animals = []string{"cat", "dog", "fish"} // 指向切片
fmt.Println((*animals)[1:3]) // ["dog" "fish"]
- 完整形式 :
s[low:high:max],额外指定容量上限,cap = max - low,可以限制子切片容量,避免意外修改底层数组或引发内存问题。
- 区间范围:
0 <= low <= high <= max <= cap(s)
- 与简单切片表达式类似,
len=high - low,但容量cap=max - low -> 4 - 1 = 3。
- 在完整切片表达式中,只有
low可以省略,缺省为 0;但high和max不允许省略
- 完整切片表达式仅支持数组、切片或指向数组的指针,不支持字符串,否则会报错
go
s := []int{2, 3, 5, 7, 11}
s4 := s[1:3:4]
fmt.Println(s4, "| len:", len(s4), "cap:", cap(s4)) // [3 5] | len: 2 cap: 3
2.2.2 追加和扩容
append() 向切片追加元素,超过容量时自动扩容
go
package main
import "fmt"
func main() {
// 从 nil 切片开始 append(完全合法)
var numbers []int
numbers = append(numbers, 1) // 追加单个元素
numbers = append(numbers, 2, 3, 4) // 追加多个元素
// 用 ... 展开切片追加
moreNumbers := []int{5, 6, 7}
numbers = append(numbers, moreNumbers...) // 展开追加
// 观察容量增长:make([]string, 0, 2)
fruits := make([]string, 0, 2) // 长度0,容量2
fmt.Printf("Initial - len: %d, cap: %d\n", len(fruits), cap(fruits)) // Initial - len: 0, cap: 2
fruits = append(fruits, "apple")
fmt.Printf("After 1 append - len: %d, cap: %d\n", len(fruits), cap(fruits)) // After 1 append - len: 1, cap: 2
fruits = append(fruits, "banana")
fmt.Printf("After 2 appends - len: %d, cap: %d\n", len(fruits), cap(fruits)) // After 2 appends - len: 2, cap: 2
// 第3次 append 超出容量,触发扩容:2 → 4
fruits = append(fruits, "cherry")
fmt.Printf("After 3 appends - len: %d, cap: %d\n", len(fruits), cap(fruits)) After 3 appends - len: 3, cap: 4
// nil 切片可以直接 append
var colors []string // nil 切片
colors = append(colors, "red") // append 自动分配底层数组
fmt.Println("Numbers:", numbers) // Numbers: [1 2 3 4 5 6 7]
fmt.Println("Fruits:", fruits) // Fruits: [apple banana cherry]
fmt.Println("Colors:", colors) // Colors: [red]
}
append(slice, element)--- 返回新切片(长度+1),必须用=接住返回值
- 扩容策略:通常翻倍(2→4→8→16...),但 Go 运行时可能根据情况调整
2.2.1 删除指定索引的元素 --- append 切片拼接
Go 没有内置的"删除元素"操作,用 append 把前半段和后半段拼接起来。Go 删除切片元素的标准模式:append 前半段 + 后半段。
go
package main
import "fmt"
func main() {
data := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
// 删除索引3的元素(值=4)
// append(data[:3], data[4:]...) → [1,2,3] + [5,6,7,8,9,10]
index := 3
data = append(data[:index], data[index+1:]...)
fmt.Println("After removing index 3:", data) // After removing index 3: [1 2 3 5 6 7 8 9 10]
}
2.2.2 在指定索引插入元素 --- 双层 append
插入元素也用 append,分三步:取前半段 → append 新值 → append 后半段
go
package main
import "fmt"
func main() {
data := []int{1, 2, 3, 5, 6, 7, 8, 9, 10} // 删除4之后的切片
// 在索引2处插入值99
insertIndex := 2
insertValue := 99
// 步骤拆解:
// 1. append(data[:2], 99) → [1, 2, 99]
// 2. append([1, 2, 99], data[2:]...) → [1, 2, 99, 3, 5, 6, 7, 8, 9, 10]
data = append(append(data[:insertIndex], insertValue), data[insertIndex:]...)
fmt.Println("After inserting 99 at index 2:", data)
}
2.3 切片拷贝
切片的"拷贝"有浅拷贝(共享底层数组)和深拷贝(独立底层数组)两种。
- 浅拷贝
original[:]--- 新切片头 + 共享底层数组,修改互相影响
- 深拷贝
copy(dest, src)--- 创建独立底层数组,修改互不影响。返回实际复制的元素数 = min(len(dest), len(src))
- 目标长度3,源长度5:只复制3个 →
[a b c],所以copy不会扩容目标切片!目标切片必须预先make并有足够长度
- 源长度2,目标长度5:复制2个,剩余保持零值 →
[100 200 0 0 0]
go
package main
import "fmt"
func main() {
original := []int{1, 2, 3, 4, 5}
// 浅拷贝:original[:] 创建新切片头,但共享底层数组
shallow := original[:]
shallow[0] = 999 // 修改 shallow 也修改 original!
fmt.Println("After modifying shallow copy:")
fmt.Println("Original:", original) // [999 2 3 4 5] --- 被修改了
fmt.Println("Shallow:", shallow) // [999 2 3 4 5]
// 重置 original
original[0] = 1
// 深拷贝:copy() 创建独立底层数组
deep := make([]int, len(original)) // 先创建目标切片
copy(deep, original) // copy(dest, src) --- 复制元素
deep[0] = 888 // 修改 deep 不影响 original
fmt.Println("\nAfter modifying deep copy:")
fmt.Println("Original:", original) // [1 2 3 4 5] --- 未被修改
fmt.Println("Deep:", deep) // [888 2 3 4 5]
// copy 返回复制的元素数量
source := []string{"a", "b", "c", "d", "e"}
destination := make([]string, 3) // 目标长度3
copied := copy(destination, source) // 只能复制3个(目标容量限制)
fmt.Printf("\nCopied %d elements: %v\n", copied, destination)
// 小切片复制到大切片
small := []int{100, 200}
large := make([]int, 5)
n := copy(large, small) // 只复制2个,剩余3个保持零值0
fmt.Printf("Copied %d elements to large slice: %v\n", n, large)
}
2.4 切片容量与底层共享
切片容量是从切片起始位置到底层数组末尾的元素数量,理解容量是理解切片的关键
go
package main
import "fmt"
func main() {
// make 指定长度和容量
slice1 := make([]int, 5, 10) // 5个零值元素,底层数组容量10
fmt.Printf("slice1 - len: %d, cap: %d, slice: %v\n", len(slice1), cap(slice1), slice1) // slice1 - len: 5, cap: 10, slice: [0 0 0 0 0]
for i := range slice1 {
slice1[i] = i * 2
}
fmt.Printf("After filling: %v\n", slice1) // After filling: [0 2 4 6 8]
// 从数组切片:容量 = 底层数组从起始位置到末尾的长度
array := [10]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
slice2 := array[2:6] // 索引2到5,长度4
// 容量 = 10 - 2 = 8(从索引2到数组末尾)
fmt.Printf("slice2 - len: %d, cap: %d, slice: %v\n", len(slice2), cap(slice2), slice2) // slice2 - len: 4, cap: 8, slice: [3 4 5 6]
// 修改切片会影响原数组
slice2[0] = 999 // array[2] 也变成 999
fmt.Printf("Array after modifying slice2: %v\n", array) // Array after modifying slice2: [1 2 999 4 5 6 7 8 9 10]
fmt.Printf("slice2 after modification: %v\n", slice2) // slice2 after modification: [999 4 5 6]
// append 超出容量时触发扩容
smallSlice := make([]int, 2, 3) // 长度2,容量3
fmt.Printf("Initial smallSlice - len: %d, cap: %d\n", len(smallSlice), cap(smallSlice)) // Initial smallSlice - len: 2, cap: 3
smallSlice[0] = 10
smallSlice[1] = 20
fmt.Println("Appending elements and observing capacity:")
// 第1次 append:还在容量内
smallSlice = append(smallSlice, 30)
fmt.Printf("After append(30) - len: %d, cap: %d, slice: %v\n", len(smallSlice), cap(smallSlice), smallSlice) // After append(30) - len: 3, cap: 3, slice: [10 20 30]
// 第2次 append:超出容量,扩容 3→6
smallSlice = append(smallSlice, 40)
fmt.Printf("After append(40) - len: %d, cap: %d, slice: %v\n", len(smallSlice), cap(smallSlice), smallSlice) // After append(40) - len: 4, cap: 6, slice: [10 20 30 40]
// 第3次 append 多个元素:7个 > 容量6,扩容 6→12
smallSlice = append(smallSlice, 50, 60, 70)
fmt.Printf("After append(50,60,70) - len: %d, cap: %d, slice: %v\n", len(smallSlice), cap(smallSlice), smallSlice) // After append(50,60,70) - len: 7, cap: 12, slice: [10 20 30 40 50 60 70]
}
2.5 切片自定义排序
2.5.1 自定义排序函数
sort.Slice(slice, lessFunc) 用自定义的比较函数对切片排序,lessFunc 返回 true 表示 i 应排在 j 前面。
go
package main
import (
"fmt"
"sort"
)
type Person struct {
Name string
Age int
Salary int
}
func main() {
people := []Person{
{"Alice", 28, 75000},
{"Bob", 32, 85000},
{"Charlie", 25, 65000},
{"Diana", 29, 90000},
{"Eve", 25, 70000},
}
fmt.Println("Original people:")
printPeople(people)
// 按年龄升序:people[i].Age < people[j].Age → i 排前面
sort.Slice(people, func(i, j int) bool {
return people[i].Age < people[j].Age
})
fmt.Println("\nSorted by age (ascending):")
printPeople(people)
}
2.5.2 稳定排序(保持相等元素的原始顺序)
sort.SliceStable 在排序时保持相等元素的原始相对顺序。
go
package main
import (
"fmt"
"sort"
)
type Person struct {
Name string
Age int
Salary int
}
func printPeople(people []Person) {
for _, p := range people {
fmt.Printf(" %s (Age: %d, Salary: $%d)\n", p.Name, p.Age, p.Salary)
}
}
func main() {
// 先按名字排序,建立初始顺序
people := []Person{
{"Alice", 28, 75000},
{"Bob", 32, 85000},
{"Charlie", 25, 65000},
{"Diana", 29, 90000},
{"Eve", 25, 70000},
}
// 多级排序:先按年龄,年龄相同则按薪资降序
sort.SliceStable(people, func(i, j int) bool {
if people[i].Age == people[j].Age {
return people[i].Salary > people[j].Salary // 同年龄,薪资高排前面
}
return people[i].Age < people[j].Age // 不同年龄,年龄小排前面
})
fmt.Println("Sorted by age, then salary (stable sort):")
printPeople(people)
}
2.5.3 检查是否已按自定义规则排序
sort.SliceIsSorted(slice, lessFunc) 是检查切片是否已按自定义规则排序。比较函数与sort.Slice使用相同的逻辑:ages[i] <= ages[j]表示升序。(注意用<= 而不是<:<= 允许相等元素,< 会认为相等元素是"未排序")。
返回 true 表示切片已按该规则排序,false 表示未排序
go
package main
import (
"fmt"
"sort"
)
func main() {
ages := []int{25, 30, 35, 40, 45}
isSorted := sort.SliceIsSorted(ages, func(i, j int) bool {
return ages[i] <= ages[j] // 升序规则(注意用 <= 而不是 <)
})
fmt.Printf("Ages %v is sorted: %t\n", ages, isSorted)
}
2.6 数组和切片的区别
| 对比项 | 数组 | 切片 |
|---|---|---|
| 类型 | [N]T |
[]T |
| 长度 | 固定,编译期确定,是类型的一部分 | 动态,运行时由 len 决定 |
| 示例 | [3]int、[5]string |
[]int、[]string |
| 是否可增长 | 不可以 | 可以用 append 扩容 |
| 内存情况 | 一段连续内存,直接存放元素 | 切片本身不直接存数据,它只是一个"切片头",通常包含:- 指向底层数组的指针- 长度 len- 容量 cap |
| 赋值情况 | - 值类型,赋值或传参时会复制整个数组 。- [3]int 和 [4]int 是不同类型 ,不能互相赋值 |
- 值类型,但复制的是切片头,底层数组仍然共享 。- []int 不关心长度,任意长度的 []int 都是同一类型,可以相互赋值 |
| 比较 | 如果元素类型可比较,数组可以用 == 比较、 |
只能和 nil 比较,不能直接用 == 比较两个切片。Go 1.21 后可以用 slices.Equal(s1, s2) 比较切片内容。 |
3. Map
- Map 是一种无序的键值对集合,零值是 nil,可按「键」快速查找「值」。
- Map 是引用类型,赋值或传参时传递的是引用(底层指向同一个哈希表)。
- Map 的键必须是可比较的类型 (如布尔、数字、字符串、指针、通道、接口、结构体、数组等),不能是slice切片、map、函数(因为它们不可比较);值可以是任意类型。
- Map 只能与
nil比较,不能互相比较。
3.1 声明和初始化
- 声明一个 nil map
只声明不初始化的 map 是 nil,可以读、可以 len 、可以 range 、可以 delete ,但****不能写入元素。
go
// 不可以直接对 nil map 赋值
var m map[string]int // m 是 nil map
m["a"] = 1 // panic: assignment to entry in nil map
- 先声明再赋值
声明后必须让 map 变成非 nil,才能往里面写键值对。
go
// 方式一:声明后用 make 初始化
var m map[string]int
m = make(map[string]int) // 初始化
m["a"] = 1 // OK
// 方式二:声明后直接赋一个字面量 map
var m map[string]int
m = map[string]int{"a": 1} // OK
m["b"] = 2 // OK
// 方式三:声明时直接初始化
m := make(map[string]int)
m["a"] = 1
// 或
m := map[string]int{"a": 1}
nil map 与空 map
var m map[string]int是 nil map,不能写 ,但可读、可len、可range。只声明,没有分配底层哈希表
make(map[string]int)是空 map,可读写 。调用make,分配了底层哈希表。
- 字面量
go
m := map[string]int{
"apple": 1,
"banana": 2,
}
- make创建
go
m := make(map[string]int) // 空 map,可读写
m2 := make(map[string]int, 10) // 预分配容量约 10
m["a"] = 1
3.2 基本操作
go
// 1.增/改
m["orange"] = 3
m["apple"] = 100 // 已存在则覆盖
// 2.查
// 方式一:不存在时返回零值
v := m["apple"]
// 方式二:判断键是否存在,用"逗号 ok"语法
v, ok := m["apple"]
if ok {
fmt.Println("存在,值为", v)
} else {
fmt.Println("不存在")
}
// 3.删:
delete(m, "apple")
// 4.遍历
for k, v := range m {
fmt.Println(k, v)
}
// 只遍历键
for k := range m { ... }
// 只遍历值
for _, v := range m { ... }
// 5.获取长度
length :=len(m)
- 删除不存在的键不会报错。
- 遍历顺序是随机的,每次运行可能不同,不能依赖顺序。
- map 不是并发安全的 。多个 goroutine 同时读写会触发 panic。可以使用
sync.RWMutex加锁;使用sync.Map(适合读多写少或键值对固定的场景);使用分片锁等自定义方案解决。