📦 第04课:数组/切片/映射

Go基础 第1阶段·第4/7课

📋 本课目标

🔬 代码演示与验证

package main
import ("fmt";"sort")
func main() {
    fmt.Println("=== 数组 ===")
    arr := [5]int{1,2,3,4,5}; fmt.Printf("数组:%v len=%d\n",arr,len(arr))
    fmt.Println("\n=== 切片 ===")
    s1 := []int{10,20,30,40,50}; fmt.Printf("切片:%v len=%d cap=%d\n",s1,len(s1),cap(s1))
    s2 := make([]int,3,10); fmt.Printf("make:len=%d cap=%d\n",len(s2),cap(s2))
    s3 := []int{0,1,2,3,4,5,6,7,8,9}
    fmt.Printf("s3[2:5]=%v s3[:3]=%v s3[7:]=%v\n",s3[2:5],s3[:3],s3[7:])
    s4 := []int{1,2,3}; s4=append(s4,4,5,6); fmt.Printf("append:%v\n",s4)
    src:=[]int{1,2,3,4,5}; dst:=make([]int,3); n:=copy(dst,src); fmt.Printf("copy:dst=%v n=%d\n",dst,n)
    s5:=[]int{1,2,3,4,5}; i:=2; s5=append(s5[:i],s5[i+1:]...); fmt.Printf("删除idx2:%v\n",s5)
    fmt.Println("\n=== 映射 ===")
    m1:=map[string]int{"Go":2009,"Python":1991,"Rust":2010}; fmt.Printf("map:%v\n",m1)
    year,ok:=m1["Go"]; fmt.Printf("Go:year=%d ok=%t\n",year,ok)
    for k,v:=range m1{fmt.Printf("  %s→%d\n",k,v)}
    delete(m1,"Rust"); fmt.Printf("删除后:%v len=%d\n",m1,len(m1))
    fmt.Println("\n=== 排序 ===")
    nums:=[]int{5,3,8,1,9,2}; sort.Ints(nums); fmt.Printf("排序:%v\n",nums)
    strs:=[]string{"banana","apple","cherry"}; sort.Strings(strs); fmt.Printf("strs:%v\n",strs)
    fmt.Println("\n=== 二维 ===")
    matrix:=make([][]int,3)
    for i:=range matrix{matrix[i]=make([]int,3);for j:=range matrix[i]{matrix[i][j]=i*3+j}}
    for _,row:=range matrix{fmt.Println(row)}
}

🔍 运行验证

✅ 验证通过 — 实机运行结果:

=== 数组 ===
数组:[1 2 3 4 5] len=5

=== 切片 ===
切片:[10 20 30 40 50] len=5 cap=5
make:len=3 cap=10
s3[2:5]=[2 3 4] s3[:3]=[0 1 2] s3[7:]=[7 8 9]
append:[1 2 3 4 5 6]
copy:dst=[1 2 3] n=3
删除idx2:[1 2 4 5]

=== 映射 ===
map:map[Go:2009 Python:1991 Rust:2010]
Go:year=2009 ok=true
  Rust→2010
  Go→2009
  Python→1991
删除后:map[Go:2009 Python:1991] len=2

=== 排序 ===
排序:[1 2 3 5 8 9]
strs:[apple banana cherry]

=== 二维 ===
[0 1 2]
[3 4 5]
[6 7 8]

🧠 数组vs切片

数组: 固定长度/值类型/很少用 切片: 动态长度/引用类型/常用 底层: type slice struct { ptr *array; len int; cap int }

📦 切片操作

s := make([]int, 0, 10)  // 预分配
s = append(s, 1, 2, 3)
copy(dst, src)
// 删除索引i(保序): s = append(s[:i], s[i+1:]...)
💡 扩容:旧cap<256翻倍,≥256按1.25×增长。预分配cap减少扩容。

🗺️ 映射

m := map[string]int{"Go":2009}
v, ok := m["Go"]  // 双值接收
delete(m, "Go")
for k, v := range m { ... }
⚠️ map非并发安全!用sync.Map或加锁。

🔑 本课要点回顾

知识点关键内容
数组固定长度/值类型
切片动态/引用类型/底层结构
append返回新切片/扩容
映射键值对/无序/并发不安全
排序sort.Ints/Strings

🏋️ 练习

去重

有序切片原地去重

词频统计

map统计单词频率

矩阵转置

二维切片转置

🏆

成就解锁:集合操作专家!

📖 切片深入

切片底层结构

type slice struct {
    ptr *array  // 指向底层数组
    len int     // 当前元素数
    cap int     // 底层数组容量
}

// 扩容策略 (Go 1.18+)
// 旧cap < 256: 新cap = 旧cap * 2
// 旧cap >= 256: 新cap = 旧cap * 1.25

切片共享陷阱

// ⚠️ 切片共享底层数组!
arr := [5]int{1,2,3,4,5}
s1 := arr[1:3]  // [2,3]
s2 := arr[2:4]  // [3,4]
s1[1] = 99      // arr变成[1,2,99,4,5]
fmt.Println(s2[0])  // 99!s2也被影响

// ✅ 用copy避免共享
s3 := make([]int, 2)
copy(s3, arr[1:3])

🗺️ 映射高级

// 设置存在检查
if v, ok := m["key"]; ok {
    fmt.Println("存在:", v)
} else {
    fmt.Println("不存在")
}

// 按key排序遍历
keys := make([]string, 0, len(m))
for k := range m { keys = append(keys, k) }
sort.Strings(keys)
for _, k := range keys { fmt.Println(k, m[k]) }

// map作为集合(set)
set := make(map[string]bool)
set["item"] = true
if set["item"] { fmt.Println("存在") }

// 分组
groups := make(map[string][]string)
for _, item := range items {
    groups[item.Category] = append(groups[item.Category], item.Name)
}

📖 延伸阅读与资源

🎯 实战开发建议

  1. 动手编码 — 只看不练等于没学,每课代码必须自己敲一遍
  2. 使用go docgo doc fmt.Printf 查看标准库文档
  3. 编写测试go test 验证你的理解是否正确
  4. 阅读源码 — 标准库是Go最佳实践的活教材
  5. 项目实战 — 在真实项目中应用所学

⚡ Go性能优化清单

// 1. 预分配切片容量 — 避免频繁扩容
s := make([]int, 0, expectedSize)

// 2. strings.Builder拼接 — 比fmt.Sprintf快3-5倍
var b strings.Builder
b.WriteString("hello")
b.WriteString(" world")
result := b.String()

// 3. sync.Pool对象复用 — 减少GC压力
var bufPool = sync.Pool{
    New: func() interface{} { return new(bytes.Buffer) },
}
buf := bufPool.Get().(*bytes.Buffer)
defer bufPool.Put(buf)

// 4. 减少堆分配 — 小对象栈分配,避免逃逸
// 用 go build -gcflags="-m" 分析逃逸

// 5. 使用benchmark验证性能
func BenchmarkXxx(b *testing.B) {
    for i := 0; i < b.N; i++ {
        // 被测代码
    }
}
// go test -bench=. -benchmem

🔍 调试与排查技巧

// 1. 格式化调试输出
fmt.Printf("%+v
", myStruct)  // 带字段名
fmt.Printf("%#v
", myValue)   // Go语法表示

// 2. log包运行时日志
log.Printf("[DEBUG] value=%v", val)
log.SetFlags(log.LstdFlags | log.Lshortfile)

// 3. delve源码调试器
// 安装: go install github.com/go-delve/delve/cmd/dlv@latest
// 使用: dlv debug ./main.go
// 命令: break main.main / continue / next / print

// 4. pprof性能分析
import _ "net/http/pprof"
go http.ListenAndServe(":6060", nil)
// 访问: http://localhost:6060/debug/pprof/
// 命令: go tool pprof http://localhost:6060/debug/pprof/profile

// 5. race detector竞态检测
// go run -race main.go
// go test -race ./...

📊 Go工具生态

工具用途安装
golangci-lint代码静态检查go install github.com/golangci/golangci-lint/cmd/...@latest
gofumpt代码格式化(增强)go install mvdan.cc/gofumpt@latest
goimports自动管理importgo install golang.org/x/tools/cmd/goimports@latest
dlv源码调试器go install github.com/go-delve/delve/cmd/dlv@latest
air热重载开发go install github.com/cosmtrek/air@latest
swagSwagger文档生成go install github.com/swaggo/swag/cmd/swag@latest
migrate数据库迁移go install -tags 'postgres' github.com/golang-migrate/migrate/v4/cmd/migrate@latest

🏗️ Go项目结构最佳实践

myproject/
├── cmd/                    # 可执行程序入口
│   └── server/
│       └── main.go
├── internal/               # 私有代码(不可外部导入)
│   ├── handler/            # HTTP处理
│   ├── service/            # 业务逻辑
│   ├── repository/         # 数据访问
│   └── model/              # 数据模型
├── pkg/                    # 可复用公共包
│   └── utils/
├── api/                    # API定义(proto/openapi)
├── configs/                # 配置文件
├── scripts/                # 构建/部署脚本
├── test/                   # 集成测试
├── go.mod
├── go.sum
├── Makefile
└── Dockerfile

🌐 Go Web开发生态

类别推荐方案替代方案
Web框架GinEcho / Chi / Fiber
ORMGORMEnt / sqlx / sqlc
配置Viperkoanf
日志zapzerolog / slog
验证validatorgo-playground/validator
缓存go-redisredigo
JWTgolang-jwt
Swaggerswag
测试testify
Mockgomocktestify/mock

💡 编码规范

// 命名规范
// 包名: 小写单词, 不用下划线  package httputil
// 导出: 大写开头              func CreateUser()
// 未导出: 小写开头            func validateInput()
// 接口: -er后缀              type Reader interface{}
// 常量: 驼峰                 const maxRetries = 3
// 枚举: 类型+常量            type Role int; const Admin Role = 0

// 错误处理规范
// 错误变量: Err前缀           var ErrNotFound = errors.New("...")
// 错误类型: Error后缀         type ValidationError struct{...}
// 包装: %w                    fmt.Errorf("查询失败: %w", err)

// 注释规范
// 导出符号必须有注释
// 注释以符号名开头            // CreateUser creates a new user.
// 包注释: doc.go              // Package user handles user operations.