当前位置: 首页 > news >正文

【Go】--数组和切片

文章目录

  • Go 语言数组与切片
    • 数组和切片
    • 数组 (Array)
      • 定义
      • 语法结构
      • 特性
      • 常见操作
    • 切片 (Slice)
      • 定义
      • 语法结构
      • 内部结构
      • 常见操作
      • 切片扩容机制
    • 数组与切片的区别与联系
      • 主要区别
      • 联系
      • 相互转换示例
    • 使用场景
      • 数组的使用场景
      • 切片的使用场景
    • 高级特性
      • 切片的内存优化
      • 多维切片
    • 注意事项
      • 数组注意事项
      • 切片注意事项
    • ALL

Go 语言数组与切片

数组和切片

数组和切片是 Go 语言中处理集合数据的核心数据结构。数组提供固定大小的存储,切片提供动态灵活的接口。理解它们的区别、联系和适用场景,对于编写高效、正确的 Go 程序至关重要。

  • 数组:适合固定大小、性能敏感的场景
  • 切片:适合动态大小、需要灵活操作的场景
  • 选择原则:根据数据大小是否固定、是否需要动态调整来选择

数组 (Array)

定义

数组是具有固定长度的相同类型元素的序列。数组的长度是类型的一部分,因此不同长度的数组是不同的类型。

语法结构

// 声明语法
var arrayName [length]elementType// 初始化语法
var arrayName [length]elementType = [length]elementType{value1, value2, ..., valueN}
arrayName := [length]elementType{value1, value2, ..., valueN}// 自动推断长度
arrayName := [...]elementType{value1, value2, ..., valueN}

特性

  1. 固定长度:数组长度在编译时确定,不可改变
  2. 值类型:数组是值类型,赋值和传参会复制整个数组
  3. 类型包含长度[3]int[5]int 是不同的类型
  4. 内存连续:数组元素在内存中连续存储
package mainimport "fmt"func main() {// 声明数组var arr1 [3]intfmt.Println("arr1:", arr1) // [0 0 0]// 初始化数组var arr2 [3]int = [3]int{1, 2, 3}arr3 := [3]int{4, 5, 6}fmt.Println("arr2:", arr2) // [1 2 3]fmt.Println("arr3:", arr3) // [4 5 6]// 自动推断长度arr4 := [...]int{7, 8, 9, 10}fmt.Println("arr4:", arr4)        // [7 8 9 10]fmt.Println("arr4 长度:", len(arr4)) // 4// 指定索引初始化arr5 := [5]int{1: 10, 3: 30}fmt.Println("arr5:", arr5) // [0 10 0 30 0]// 多维数组var matrix [2][3]int = [2][3]int{{1, 2, 3},{4, 5, 6},}fmt.Println("matrix:", matrix) // [[1 2 3] [4 5 6]]
}

常见操作

package mainimport "fmt"func main() {arr := [5]int{10, 20, 30, 40, 50}// 访问元素fmt.Println("第一个元素:", arr[0]) // 10fmt.Println("最后一个元素:", arr[len(arr)-1]) // 50// 修改元素arr[0] = 100fmt.Println("修改后:", arr) // [100 20 30 40 50]// 遍历数组fmt.Println("遍历数组:")for i := 0; i < len(arr); i++ {fmt.Printf("arr[%d] = %d\n", i, arr[i])}// 使用 range 遍历fmt.Println("使用 range 遍历:")for index, value := range arr {fmt.Printf("索引: %d, 值: %d\n", index, value)}// 数组比较(只有相同长度和类型的数组可以比较)arr1 := [3]int{1, 2, 3}arr2 := [3]int{1, 2, 3}arr3 := [3]int{1, 2, 4}fmt.Println("arr1 == arr2:", arr1 == arr2) // truefmt.Println("arr1 == arr3:", arr1 == arr3) // false
}

切片 (Slice)

定义

切片是对数组的抽象,提供了更灵活、更强大的序列接口。切片是引用类型,底层引用一个数组。

语法结构

// 声明切片
var sliceName []elementType// 使用 make 创建
sliceName := make([]elementType, length, capacity)// 字面量创建
sliceName := []elementType{value1, value2, ..., valueN}// 从数组创建
sliceName := arrayName[start:end]

内部结构

切片包含三个组件:

  • 指针:指向底层数组的起始位置
  • 长度:切片中元素的数量
  • 容量:从切片起始位置到底层数组末尾的元素数量
package mainimport "fmt"func main() {// 声明切片var slice1 []intfmt.Println("slice1:", slice1)        // []fmt.Println("slice1 是否为 nil:", slice1 == nil) // true// 使用 make 创建切片slice2 := make([]int, 3, 5)fmt.Println("slice2:", slice2)        // [0 0 0]fmt.Println("长度:", len(slice2))      // 3fmt.Println("容量:", cap(slice2))      // 5// 字面量创建slice3 := []int{1, 2, 3, 4, 5}fmt.Println("slice3:", slice3)        // [1 2 3 4 5]// 从数组创建切片arr := [5]int{10, 20, 30, 40, 50}slice4 := arr[1:4] // 包含索引1,不包含索引4fmt.Println("slice4:", slice4)        // [20 30 40]// 修改切片会影响底层数组slice4[0] = 200fmt.Println("修改后数组:", arr)        // [10 200 30 40 50]fmt.Println("修改后切片:", slice4)      // [200 30 40]
}

常见操作

package mainimport "fmt"func main() {// 创建切片slice := []int{1, 2, 3}// 添加元素slice = append(slice, 4)fmt.Println("添加后:", slice) // [1 2 3 4]// 添加多个元素slice = append(slice, 5, 6, 7)fmt.Println("添加多个后:", slice) // [1 2 3 4 5 6 7]// 合并切片slice2 := []int{8, 9, 10}slice = append(slice, slice2...)fmt.Println("合并后:", slice) // [1 2 3 4 5 6 7 8 9 10]// 复制切片slice3 := make([]int, len(slice))copy(slice3, slice)fmt.Println("复制后:", slice3) // [1 2 3 4 5 6 7 8 9 10]// 修改复制后的切片不会影响原切片slice3[0] = 100fmt.Println("原切片:", slice)   // [1 2 3 4 5 6 7 8 9 10]fmt.Println("新切片:", slice3) // [100 2 3 4 5 6 7 8 9 10]// 切片操作fmt.Println("前3个元素:", slice[:3])   // [1 2 3]fmt.Println("从第3个开始:", slice[3:])   // [4 5 6 7 8 9 10]fmt.Println("第2-5个元素:", slice[2:5]) // [3 4 5]// 删除元素(删除索引2的元素)slice = append(slice[:2], slice[3:]...)fmt.Println("删除后:", slice) // [1 2 4 5 6 7 8 9 10]
}

切片扩容机制

package mainimport "fmt"func main() {slice := make([]int, 0, 2)fmt.Printf("初始 - 长度: %d, 容量: %d\n", len(slice), cap(slice))for i := 1; i <= 10; i++ {slice = append(slice, i)fmt.Printf("添加 %d - 长度: %d, 容量: %d\n", i, len(slice), cap(slice))}/* 输出示例:初始 - 长度: 0, 容量: 2添加 1 - 长度: 1, 容量: 2添加 2 - 长度: 2, 容量: 2添加 3 - 长度: 3, 容量: 4  (扩容)添加 4 - 长度: 4, 容量: 4添加 5 - 长度: 5, 容量: 8  (扩容)添加 6 - 长度: 6, 容量: 8添加 7 - 长度: 7, 容量: 8添加 8 - 长度: 8, 容量: 8添加 9 - 长度: 9, 容量: 16 (扩容)添加 10 - 长度: 10, 容量: 16*/
}

数组与切片的区别与联系

主要区别

特性数组 (Array)切片 (Slice)
长度固定,编译时确定动态,运行时可变
类型值类型引用类型
传递值传递(复制整个数组)引用传递(传递切片头)
比较可以比较(相同类型)不能直接比较
声明var arr [3]intvar slice []int
零值对应类型的零值数组nil

联系

  1. 底层关系:切片基于数组实现,切片是数组的视图
  2. 内存共享:多个切片可以共享同一个底层数组
  3. 相互转换:数组可以转换为切片,但切片不能直接转换为数组

相互转换示例

package mainimport "fmt"func main() {// 数组转切片arr := [5]int{1, 2, 3, 4, 5}slice := arr[:] // 整个数组转为切片fmt.Println("数组转切片:", slice) // [1 2 3 4 5]// 切片转数组(需要确保长度匹配)if len(slice) == 5 {var newArr [5]intcopy(newArr[:], slice) // 将切片内容复制到数组fmt.Println("切片转数组:", newArr) // [1 2 3 4 5]}// Go 1.17+ 支持直接转换// newArr := (*[5]int)(slice) // 注意:需要长度匹配
}

使用场景

数组的使用场景

  1. 固定大小的集合:如月份、星期等固定长度的数据
  2. 性能敏感场景:数组在栈上分配,访问速度快
  3. 内存布局控制:需要精确控制内存布局时
  4. 作为切片底层:为切片提供底层存储
package mainimport "fmt"func main() {// 固定大小的数据集合daysOfWeek := [7]string{"周一", "周二", "周三", "周四", "周五", "周六", "周日"}// 矩阵运算var matrix1 [2][2]int = [2][2]int{{1, 2}, {3, 4}}var matrix2 [2][2]int = [2][2]int{{5, 6}, {7, 8}}// 性能敏感的场景var buffer [1024]byte // 固定大小的缓冲区fmt.Println("星期:", daysOfWeek)fmt.Println("矩阵1:", matrix1)fmt.Println("矩阵2:", matrix2)fmt.Println("缓冲区大小:", len(buffer))
}

切片的使用场景

  1. 动态集合:大小不确定的数据集合
  2. 函数参数:作为函数参数传递集合数据
  3. 数据流处理:处理来自网络、文件的数据流
  4. 字符串处理:字符串本质上是字节切片
package mainimport ("fmt""strings"
)func processData(data []int) []int {result := make([]int, 0, len(data))for _, value := range data {if value%2 == 0 {result = append(result, value*2)}}return result
}func main() {// 动态数据处理data := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}processed := processData(data)fmt.Println("处理后的数据:", processed) // [4 8 12 16 20]// 字符串处理(字符串可视为只读的字节切片)str := "Hello, 世界"bytes := []byte(str)runes := []rune(str)fmt.Println("字符串:", str)fmt.Println("字节切片:", bytes)fmt.Println("字符切片:", runes)fmt.Println("字符数量:", len(runes)) // 8 (Hello, 和 世界)// 字符串分割words := strings.Split("Go is a programming language", " ")fmt.Println("分割后的单词:", words)
}

高级特性

切片的内存优化

package mainimport "fmt"func main() {// 预分配容量避免频繁扩容slice := make([]int, 0, 1000) // 预分配足够容量for i := 0; i < 1000; i++ {slice = append(slice, i)}fmt.Printf("最终长度: %d, 容量: %d\n", len(slice), cap(slice))// 切片截取可能导致内存泄漏bigSlice := make([]int, 10000)smallSlice := bigSlice[:10] // smallSlice 仍然引用整个 bigSlice// 正确的做法:复制需要的部分properSlice := make([]int, 10)copy(properSlice, bigSlice[:10])fmt.Printf("smallSlice 容量: %d\n", cap(smallSlice))     // 10000fmt.Printf("properSlice 容量: %d\n", cap(properSlice))   // 10
}

多维切片

package mainimport "fmt"func main() {// 创建二维切片matrix := make([][]int, 3)for i := range matrix {matrix[i] = make([]int, 3)for j := range matrix[i] {matrix[i][j] = i*3 + j + 1}}fmt.Println("二维切片:")for i, row := range matrix {fmt.Printf("第%d行: %v\n", i, row)}// 不规则多维切片irregular := [][]int{{1},{2, 3},{4, 5, 6},}fmt.Println("\n不规则二维切片:")for i, row := range irregular {fmt.Printf("第%d行: %v\n", i, row)}
}

注意事项

数组注意事项

  1. 长度是类型的一部分:不同长度的数组不能相互赋值
  2. 值传递开销:大数组作为参数传递时会产生复制开销
  3. 零值初始化:声明但未初始化的数组会用零值填充
package mainimport "fmt"func main() {// 错误:不同长度的数组不能赋值// var arr1 [3]int// var arr2 [5]int// arr1 = arr2 // 编译错误// 正确:使用相同类型的数组var arr3 [3]int = [3]int{1, 2, 3}var arr4 [3]intarr4 = arr3 // 正确fmt.Println("arr4:", arr4)
}

切片注意事项

  1. nil 切片与空切片:nil 切片没有底层数组,空切片有底层数组但长度为0
  2. 切片共享底层数组:修改切片可能影响其他共享同一底层数组的切片
  3. append 的副作用:append 可能返回新的切片引用
  4. 容量与长度的区别:容量 >= 长度
package mainimport "fmt"func main() {// nil 切片 vs 空切片var nilSlice []int          // nil 切片emptySlice := []int{}        // 空切片emptySlice2 := make([]int, 0) // 空切片fmt.Printf("nilSlice: len=%d, cap=%d, nil=%t\n", len(nilSlice), cap(nilSlice), nilSlice == nil) // len=0, cap=0, nil=truefmt.Printf("emptySlice: len=%d, cap=%d, nil=%t\n", len(emptySlice), cap(emptySlice), emptySlice == nil) // len=0, cap=0, nil=falsefmt.Printf("emptySlice2: len=%d, cap=%d, nil=%t\n", len(emptySlice2), cap(emptySlice2), emptySlice2 == nil) // len=0, cap=0, nil=false// 切片共享问题arr := [5]int{1, 2, 3, 4, 5}slice1 := arr[1:4] // [2, 3, 4]slice2 := slice1[0:2] // [2, 3]slice2[0] = 200fmt.Println("arr:", arr)     // [1 200 3 4 5]fmt.Println("slice1:", slice1) // [200 3 4]fmt.Println("slice2:", slice2) // [200 3]// append 的副作用slice3 := make([]int, 2, 3)slice3[0], slice3[1] = 1, 2slice4 := append(slice3, 3)slice4[0] = 100fmt.Println("slice3:", slice3) // [1 2] (未受影响,因为容量足够)fmt.Println("slice4:", slice4) // [100 2 3]slice5 := append(slice3, 3, 4) // 超出容量,创建新底层数组slice5[0] = 200fmt.Println("slice3:", slice3) // [1 2] (未受影响)fmt.Println("slice5:", slice5) // [200 2 3 4]
}

ALL

  1. 预分配容量:使用 make([]T, 0, capacity) 预分配足够容量
  2. 避免不必要的复制:对于大切片,考虑使用指针或引用
  3. 使用数组作为切片底层:对于固定大小的数据,先创建数组再转为切片
  4. 批量操作:使用 copy 函数进行批量复制
http://www.dtcms.com/a/481676.html

相关文章:

  • 李宏毅机器学习笔记22
  • 重排反应是什么?从分子变化到四大关键特征解析
  • 服务治理与 API 网关:微服务流量管理的艺术
  • 怎样做企业的网站首页网站开发求职简历
  • 程序设计基础第2周上课前预习
  • 谷歌 chrome 浏览器安装crx插件(hackbar为例)
  • 分布式专题——43 ElasticSearch概述
  • Tomcat 启动后只显示 index.jsp,没有进入你的 Servlet 逻辑
  • 分布式之RabbitMQ的使用(3)QueueBuilder
  • 建立自己网站的好处抖音代运营可以相信吗
  • Flink 状态和 CheckPoint 的区别和联系(附源码)
  • QML学习笔记(三十六)QML的ComboBox
  • 媒介宣发的技术革命:Infoseek如何用AI重构企业传播全链路
  • uniapp开发小程序
  • 浦江县建设局网站国家企业信息信用信息公示网址
  • 2025年燃气从业人员考试真题分享
  • SuperMap iServer 数据更新指南
  • C++基础:(十三)list类的模拟实现
  • 【网络编程】从数据链路层帧头到代理服务器:解析路由表、MTU/MSS、ARP、NAT 等网络核心技术
  • 北京网站seowyhseo网站模板但没有后台如何做网站
  • 对接世界职业院校技能大赛标准,唯众打造高质量云计算实训室
  • 利用人工智能、数字孪生、AR/VR 进行军用飞机维护
  • [特殊字符] Maven 编译报错「未与 -source 8 一起设置引导类路径」完美解决方案(以芋道项目为例)
  • 【CV】泊松图像融合
  • 云智融合:人工智能与云计算融合实践指南
  • Maven创建Java项目实战全流程
  • 泉州市住房与城乡建设网站wordpress弹出搜索
  • [创业之路-691]:历史与现实的镜鉴:从三国纷争到华为铁三角的系统性启示
  • 时序数据库选型革命:深入解析Apache IoTDB的架构智慧与实战指南
  • 南通网站制作建设手机网页设计软件下载