Go 语言数组完全使用指南
一、基础操作
1. 声明和初始化
go
// 声明方式
var arr1 [5]int // 默认零值
arr2 := [3]int{1, 2, 3} // 字面量
arr3 := [...]int{1, 2, 3, 4} // 编译器推断长度
arr4 := [5]int{1, 2} // 部分初始化,其余为零值
arr5 := [5]int{2: 10, 4: 20} // 指定索引初始化
2. 访问和修改元素
go
arr := [5]int{1, 2, 3, 4, 5}
value := arr[0] // 获取索引0的值
arr[1] = 10 // 修改索引1的值
length := len(arr) // 获取数组长度
二、遍历操作
go
arr := [5]int{1, 2, 3, 4, 5}
// 方式1:for 循环
for i := 0; i < len(arr); i++ {
fmt.Println(arr[i])
}
// 方式2:range(推荐)
for index, value := range arr {
fmt.Printf("索引: %d, 值: %d\n", index, value)
}
// 方式3:只遍历值
for _, value := range arr {
fmt.Println(value)
}
三、常用操作函数
1. 比较操作
go
arr1 := [3]int{1, 2, 3}
arr2 := [3]int{1, 2, 3}
arr3 := [3]int{4, 5, 6}
equal := arr1 == arr2 // true
equal = arr1 == arr3 // false
// 注意:只有相同长度和类型的数组才能比较
2. 复制数组
go
// 值拷贝(深拷贝)
original := [3]int{1, 2, 3}
copy1 := original // 完全复制
copy2 := original // 独立副本
copy1[0] = 100 // 不影响 original
fmt.Println(original) // [1, 2, 3]
3. 数组排序(需要转换为切片)
go
import "sort"
arr := [5]int{5, 2, 8, 1, 9}
slice := arr[:] // 转换为切片
sort.Ints(slice) // 升序排序
// arr 变为 [1, 2, 5, 8, 9]
// 降序排序
sort.Sort(sort.Reverse(sort.IntSlice(slice)))
四、数组和切片的转换
go
arr := [5]int{1, 2, 3, 4, 5}
// 数组转切片
slice1 := arr[:] // 所有元素
slice2 := arr[1:4] // [2,3,4]
slice3 := arr[2:] // [3,4,5]
slice4 := arr[:3] // [1,2,3]
// 切片不能直接转数组,但可以复制
var newArr [3]int
copy(newArr[:], slice2) // 通过切片复制
五、多维数组操作
go
// 二维数组
matrix := [3][3]int{
{1, 2, 3},
{4, 5, 6},
{7, 8, 9},
}
// 遍历二维数组
for i := 0; i < len(matrix); i++ {
for j := 0; j < len(matrix[i]); j++ {
fmt.Printf("%d ", matrix[i][j])
}
fmt.Println()
}
// 使用 range
for _, row := range matrix {
for _, val := range row {
fmt.Printf("%d ", val)
}
fmt.Println()
}
六、实用工具函数
go
// 1. 查找元素
func contains(arr [5]int, target int) bool {
for _, v := range arr {
if v == target {
return true
}
}
return false
}
// 2. 求和
func sum(arr [5]int) int {
total := 0
for _, v := range arr {
total += v
}
return total
}
// 3. 最大值/最小值
func max(arr [5]int) int {
maxVal := arr[0]
for _, v := range arr {
if v > maxVal {
maxVal = v
}
}
return maxVal
}
// 4. 反转数组
func reverse(arr *[5]int) {
for i, j := 0, len(arr)-1; i < j; i, j = i+1, j-1 {
arr[i], arr[j] = arr[j], arr[i]
}
}
七、重要特性说明
-
数组是值类型:传递数组时会复制整个数组
gofunc modify(arr [3]int) { arr[0] = 100 // 不影响原数组 } -
使用指针避免复制:
gofunc modifyPtr(arr *[3]int) { arr[0] = 100 // 影响原数组 } -
数组长度是类型的一部分:
govar a [3]int var b [4]int // a = b // 编译错误:类型不同
八、最佳实践建议
- 大多数情况使用切片代替数组:切片更灵活,功能更强
- 数组适合固定大小的数据:如月份天数、RGB 颜色值
- 大数组传递时使用指针:避免性能开销
- 使用
[...T]让编译器计算长度:增加代码可维护性
go
// 推荐:让编译器计算长度
days := [...]string{"Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"}
// 不推荐:手动计数易错
days := [7]string{"Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"}
Go 语言 Map 完全使用指南
一、声明和初始化
go
// 格式:var 变量名 map[键类型]值类型
var m map[string]int // 键:string,值:int
// 此时 m = nil,不能直接赋值,会 panic
// 1. 使用 make 创建(推荐)
m1 := make(map[string]int)
m2 := make(map[string]int, 10) // 预分配容量,提高性能
// 2. 字面量创建
m3 := map[string]int{
"apple": 5,
"banana": 3,
"orange": 8,
}
// 3. 声明 nil map
var m4 map[string]int // nil map,不能直接赋值
// m4["key"] = 1 // 会 panic!
// 4. 创建空 map
m5 := map[string]int{} // 空 map,可以赋值
二、基本操作
1. 增删改查
go
m := make(map[string]int)
// 添加/修改
m["apple"] = 5 // 添加
m["apple"] = 10 // 修改
// 查询
value := m["apple"] // 如果键不存在,返回值类型的零值
value, exists := m["apple"] // 推荐:检查键是否存在
if value, ok := m["banana"]; ok {
fmt.Println("存在:", value)
} else {
fmt.Println("不存在")
}
// 删除
delete(m, "apple") // 删除键 "apple"
delete(m, "none") // 删除不存在的键,不会 panic
// 清空 map
for k := range m {
delete(m, k)
}
// 或直接重新分配
m = make(map[string]int)
2. 长度获取
go
m := map[string]int{"a": 1, "b": 2, "c": 3}
length := len(m) // 3
三、遍历操作
go
m := map[string]int{
"apple": 5,
"banana": 3,
"orange": 8,
}
// 遍历键值对
for key, value := range m {
fmt.Printf("%s: %d\n", key, value)
}
// 只遍历键
for key := range m {
fmt.Println(key)
}
// 只遍历值
for _, value := range m {
fmt.Println(value)
}
// 注意:map 遍历顺序是随机的!
// 如需有序遍历,先获取所有键并排序
四、常用操作函数
1. 键值对操作
go
// 获取所有键
func getKeys(m map[string]int) []string {
keys := make([]string, 0, len(m))
for k := range m {
keys = append(keys, k)
}
return keys
}
// 获取所有值
func getValues(m map[string]int) []int {
values := make([]int, 0, len(m))
for _, v := range m {
values = append(values, v)
}
return values
}
// 合并 map
func merge(m1, m2 map[string]int) map[string]int {
result := make(map[string]int)
for k, v := range m1 {
result[k] = v
}
for k, v := range m2 {
result[k] = v
}
return result
}
2. 查找和判断
go
// 键是否存在
if value, exists := m["key"]; exists {
// 键存在
}
// 判断 map 是否为空
func isEmpty(m map[string]int) bool {
return len(m) == 0
}
// 包含某个值
func containsValue(m map[string]int, target int) bool {
for _, v := range m {
if v == target {
return true
}
}
return false
}
五、Map 的排序
go
import "sort"
m := map[string]int{
"banana": 3,
"apple": 5,
"orange": 8,
}
// 1. 按键排序
keys := make([]string, 0, len(m))
for k := range m {
keys = append(keys, k)
}
sort.Strings(keys) // 升序
for _, k := range keys {
fmt.Printf("%s: %d\n", k, m[k])
}
// 2. 按值排序
type kv struct {
Key string
Value int
}
var sorted []kv
for k, v := range m {
sorted = append(sorted, kv{k, v})
}
sort.Slice(sorted, func(i, j int) bool {
return sorted[i].Value < sorted[j].Value // 按值升序
})
六、嵌套 Map
go
// 二维 map
nested := make(map[string]map[string]int)
// 初始化内层 map
nested["fruit"] = make(map[string]int)
nested["fruit"]["apple"] = 5
nested["fruit"]["banana"] = 3
// 更安全的访问
if fruitMap, ok := nested["fruit"]; ok {
if value, ok := fruitMap["apple"]; ok {
fmt.Println(value)
}
}
// 字面量创建
nested2 := map[string]map[string]int{
"fruit": {
"apple": 5,
"banana": 3,
},
"vegetable": {
"carrot": 10,
},
}
七、高级用法
1. Map 作为集合(Set)
go
// Go 没有内置 Set,用 map 模拟
set := make(map[string]bool)
// 添加元素
set["apple"] = true
set["banana"] = true
// 检查存在
if set["apple"] {
fmt.Println("apple 存在")
}
// 删除元素
delete(set, "apple")
// 遍历集合
for key := range set {
fmt.Println(key)
}
// 使用空结构体节省内存
set2 := make(map[string]struct{})
set2["apple"] = struct{}{}
2. Map 作为缓存
go
type Cache struct {
data map[string]interface{}
mu sync.RWMutex // 并发安全
}
func NewCache() *Cache {
return &Cache{
data: make(map[string]interface{}),
}
}
func (c *Cache) Set(key string, value interface{}) {
c.mu.Lock()
defer c.mu.Unlock()
c.data[key] = value
}
func (c *Cache) Get(key string) (interface{}, bool) {
c.mu.RLock()
defer c.mu.RUnlock()
val, ok := c.data[key]
return val, ok
}
3. 值为函数
go
// 函数映射
type Handler func(string) string
handlers := map[string]Handler{
"upper": func(s string) string { return strings.ToUpper(s) },
"lower": func(s string) string { return strings.ToLower(s) },
"reverse": func(s string) string {
runes := []rune(s)
for i, j := 0, len(runes)-1; i < j; i, j = i+1, j-1 {
runes[i], runes[j] = runes[j], runes[i]
}
return string(runes)
},
}
// 调用
if handler, ok := handlers["upper"]; ok {
result := handler("hello") // "HELLO"
}
八、并发安全
go
// 普通 map 非并发安全(会 panic)
// 方式1:使用 sync.RWMutex
type SafeMap struct {
mu sync.RWMutex
m map[string]int
}
func (s *SafeMap) Get(key string) (int, bool) {
s.mu.RLock()
defer s.mu.RUnlock()
val, ok := s.m[key]
return val, ok
}
func (s *SafeMap) Set(key string, value int) {
s.mu.Lock()
defer s.mu.Unlock()
s.m[key] = value
}
// 方式2:使用 sync.Map(适合读多写少)
var syncMap sync.Map
// 存储
syncMap.Store("key", "value")
// 加载
if value, ok := syncMap.Load("key"); ok {
fmt.Println(value)
}
// 删除
syncMap.Delete("key")
// 遍历
syncMap.Range(func(key, value interface{}) bool {
fmt.Println(key, value)
return true // 返回 false 停止遍历
})
九、性能优化技巧
go
// 1. 预分配容量
m := make(map[string]int, 1000) // 避免频繁扩容
// 2. 使用适当的数据类型
// 整型键比字符串快
m := make(map[int]string)
// 3. 避免在循环中创建 map
// 不好
for i := 0; i < 1000; i++ {
m := make(map[string]int) // 重复创建
}
// 好
m := make(map[string]int)
for i := 0; i < 1000; i++ {
m["key"] = i // 复用
delete(m, "key")
}
// 4. 复用 map
func clearMap(m map[string]int) {
for k := range m {
delete(m, k)
}
// 相比重新分配,删除所有元素可能更高效
}
十、注意事项和最佳实践
go
// 1. nil map 检查
func processMap(m map[string]int) {
if m == nil {
m = make(map[string]int) // 初始化 nil map
}
m["key"] = 1
}
// 2. 读取不存在的键返回零值
m := map[string]int{}
value := m["none"] // 0,不会报错
// 3. map 不能直接比较
// m1 == m2 // 编译错误!只能和 nil 比较
func equal(m1, m2 map[string]int) bool {
if len(m1) != len(m2) {
return false
}
for k, v1 := range m1 {
if v2, ok := m2[k]; !ok || v1 != v2 {
return false
}
}
return true
}
// 4. 使用复合字面量初始化
m := map[string]int{
"key1": 1,
"key2": 2,
// 注意最后的逗号必须有
}
十一、实用示例
go
// 1. 统计字符频率
func charCount(s string) map[rune]int {
count := make(map[rune]int)
for _, ch := range s {
count[ch]++
}
return count
}
// 2. 分组
func groupByLength(words []string) map[int][]string {
groups := make(map[int][]string)
for _, word := range words {
length := len(word)
groups[length] = append(groups[length], word)
}
return groups
}
// 3. 反转 map(值唯一时)
func reverseMap(m map[string]int) map[int]string {
reversed := make(map[int]string, len(m))
for k, v := range m {
reversed[v] = k
}
return reversed
}
// 4. 深拷贝 map
func deepCopy(original map[string]int) map[string]int {
copy := make(map[string]int, len(original))
for k, v := range original {
copy[k] = v
}
return copy
}
总结要点
- 使用
make创建,避免 nil map - 读取时用
value, ok := m[key]判断存在性 - 遍历顺序随机,需要排序时先获取键切片
- map 是引用类型,传递时不复制内容
- 非并发安全 ,并发场景使用
sync.Map或互斥锁 - 不能使用
==比较,需要自己实现比较函数 - 预分配容量提升性能
- 值类型可以是任意类型,包括函数、结构体等