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%+场景)。
相关推荐
岁忧5 小时前
(LeetCode 面试经典 150 题) 200. 岛屿数量(深度优先搜索dfs || 广度优先搜索bfs)
java·c++·leetcode·面试·go·深度优先
程序员爱钓鱼7 小时前
Go语言实战案例- 命令行参数解析器
后端·google·go
墩墩分墩20 小时前
【Go语言入门教程】 Go语言的起源与技术特点:从诞生到现代编程利器(一)
开发语言·后端·golang·go
程序员爱钓鱼1 天前
Go语言实战案例- 开发一个ToDo命令行工具
后端·google·go
科技树支点1 天前
无GC的Java创新设计思路:作用域引用式自动内存管理
java·python·go·web·编程语言·编译器
qq_172805591 天前
GO : cannot find module
学习·go
Craze_rd1 天前
Go 开发规范1
go·代码规范
saberc81 天前
【vibe coding系列】0行代码编写,使用Go+Vue3+Flutter从0到1开发小绿书(一)
后端·go
_新一2 天前
Go Slice源码解析
后端·go
_新一2 天前
Go Context源码学习
后端·go