Golang 数组、切片的实际应用

在Go语言中,数组和切片是两种核心的数据结构。它们在实际开发中有不同的应用场景和优势。本篇文章带你详细了解。

1. 数组(Array)

特点

  • 固定长度(长度是类型的一部分,如 [5]int[10]int 是不同类型)
  • 值类型(赋值或传参时会复制整个数组)
  • 内存连续分配,访问高效

实际应用场景

(1) 存储固定大小的数据集合

go 复制代码
// 存储一周的每日温度
var weeklyTemps [7]float64
weeklyTemps = [7]float64{18.5, 20.1, 22.3, 21.7, 19.4, 17.8, 16.2}

(2) 内存敏感场景(避免堆分配)

数组在栈上分配,适合小规模固定数据:

go 复制代码
// 存储RGBA像素值(固定4字节)
type Pixel [4]byte // R, G, B, A
pixel := Pixel{255, 0, 0, 255} // 红色

(3) 加密/编码等底层操作

需精确控制内存布局时:

go 复制代码
// MD5哈希值存储(固定16字节)
var hash [16]byte
copy(hash[:], md5.Sum(data))

2. 切片(Slice)

特点

  • 动态长度(底层引用数组,可动态扩容)
  • 引用类型(传递时仅复制切片头,不复制底层数据)
  • 内置操作(append, 切片表达式 s[i:j]

实际应用场景

(1) 动态数据集合(最常见用法)

go 复制代码
// 读取文件内容(长度未知)
data, _ := os.ReadFile("file.txt") // data 是 []byte 切片
lines := strings.Split(string(data), "\n")

// 动态添加用户
users := []string{"Alice", "Bob"}
users = append(users, "Charlie") // 自动扩容

(2) 复用内存(避免复制)

go 复制代码
// 截取子集(共享底层数组)
log := []byte("2023-10-01 ERROR: Disk full")
errorMsg := log[11:] // "ERROR: Disk full"(无数据复制)

(3) 实现栈/队列

go 复制代码
// 栈操作
stack := []int{}
stack = append(stack, 1)      // 压栈
top := stack[len(stack)-1]     // 取栈顶
stack = stack[:len(stack)-1]  // 弹栈

// 队列(需谨慎,出队可能导致底层数组移位)
queue := []int{1,2,3}
queue = queue[1:] // 出队(2,3)

(4) 高性能批量处理

go 复制代码
// 分批处理任务(复用内存)
batchSize := 100
data := make([]int, 0, 10000) // 预分配容量
for i := 0; i < 10000; i++ {
    data = append(data, i)
    if len(data) == batchSize {
        processBatch(data) // 处理批次
        data = data[:0]   // 重置切片(底层数组保留)
    }
}

(5) 避免内存泄漏

go 复制代码
func ReadBigFile() []byte {
    data := make([]byte, 0, 10*1024*1024) // 预分配10MB
    // ...填充数据...
    return data[:len(data)] // 返回实际大小的切片
}

特性 数组 (Array) 切片 (Slice)
长度 固定(编译时确定) 动态(运行时可扩展)
内存分配 值类型(复制整个数据) 引用类型(复制切片头)
传递开销 大(适合小数组) 小(适合大型数据)
适用场景 固定大小、内存敏感、栈分配 动态数据、共享内存、高频操作

3. 最佳实践

  1. 优先用切片:95%的场景使用切片(动态性更灵活)。

  2. 预分配容量

    go 复制代码
    // 已知最大规模时预分配
    s := make([]int, 0, 1000) // 长度0,容量1000
  3. 避免意外修改

    go 复制代码
    // 返回切片的拷贝(防止外部修改底层数组)
    func SafeReturn() []int {
        data := []int{1, 2, 3}
        return append([]int{}, data...) // 复制数据
    }
  4. 数组转切片

    go 复制代码
    arr := [3]int{1,2,3}
    slice := arr[:] // 转为切片(共享内存)

4. 性能陷阱

应用时不够谨慎可能会带来一些麻烦:

  • 切片扩容append 可能触发复制(容量不足时)。

  • 大数组传参:值复制代价高,应改用切片或指针。

  • 内存泄漏 :大切片的小子切片会阻止整个底层数组被GC回收:

    go 复制代码
    bigData := make([]byte, 100*1024*1024) // 100MB
    smallPart := bigData[:10] // 保留整个100MB内存!
    // 解决方案:复制需要的数据
    smallPart := make([]byte, 10)
    copy(smallPart, bigData)

以上就是二者对比。

总结

  • 数组:固定大小数据、栈分配敏感、精确内存控制(如加密/硬件交互)。
  • 切片:动态数据集合、高效内存复用、数据分块处理(90%+场景)。
相关推荐
Warson_L9 小时前
go的ctx超时控制
go
程序员爱钓鱼16 小时前
Go语言实战案例-复制文件内容
后端·google·go
程序员爱钓鱼16 小时前
Go语言实战案例-向文件写入内容
后端·google·go
DURUII1 天前
[Go语言基础] Context 源码剖析解读 + 用法代码 + 理解 Golang 语法特性与设计哲学
go
程序员爱钓鱼2 天前
Go语言实战案例-判断一个数是否为质数
后端·google·go
程序员爱钓鱼2 天前
Go语言实战案例-读取本地文本文件内容
后端·google·go
指月小筑2 天前
K8s 自定义调度器 Part1:通过 Scheduler Extender 实现自定义调度逻辑
云原生·容器·kubernetes·go
mCell2 天前
Go 并发编程基础:从 Goroutine 到 Worker Pool 实践
后端·性能优化·go
ん贤2 天前
如何加快golang编译速度
后端·golang·go