554 lines
14 KiB
Go
554 lines
14 KiB
Go
/*
|
||
01-arrays.go - Go 语言数组详解
|
||
|
||
学习目标:
|
||
1. 理解数组的概念和特性
|
||
2. 掌握数组的声明和初始化
|
||
3. 学会数组的基本操作
|
||
4. 了解数组的内存布局
|
||
5. 掌握数组的实际应用场景
|
||
|
||
知识点:
|
||
- 数组的定义和特性
|
||
- 数组的声明和初始化方式
|
||
- 数组的访问和修改
|
||
- 数组的长度和容量
|
||
- 多维数组
|
||
- 数组的比较和拷贝
|
||
- 数组 vs 切片的区别
|
||
*/
|
||
|
||
package main
|
||
|
||
import "fmt"
|
||
|
||
func main() {
|
||
fmt.Println("=== Go 语言数组详解 ===\n")
|
||
|
||
// 演示数组的基本概念
|
||
demonstrateBasicArrays()
|
||
|
||
// 演示数组的声明和初始化
|
||
demonstrateArrayDeclaration()
|
||
|
||
// 演示数组的操作
|
||
demonstrateArrayOperations()
|
||
|
||
// 演示多维数组
|
||
demonstrateMultiDimensionalArrays()
|
||
|
||
// 演示数组的特性
|
||
demonstrateArrayProperties()
|
||
|
||
// 演示数组的实际应用
|
||
demonstratePracticalApplications()
|
||
|
||
// 演示数组 vs 切片
|
||
demonstrateArrayVsSlice()
|
||
}
|
||
|
||
// demonstrateBasicArrays 演示数组的基本概念
|
||
func demonstrateBasicArrays() {
|
||
fmt.Println("1. 数组的基本概念:")
|
||
|
||
// 数组的基本特性
|
||
fmt.Printf(" 数组的基本特性:\n")
|
||
fmt.Printf(" - 固定长度的元素序列\n")
|
||
fmt.Printf(" - 元素类型相同\n")
|
||
fmt.Printf(" - 长度是类型的一部分\n")
|
||
fmt.Printf(" - 零值是所有元素都为零值的数组\n")
|
||
fmt.Printf(" - 值类型,赋值和传参会拷贝整个数组\n")
|
||
|
||
// 基本数组示例
|
||
fmt.Printf(" 基本数组示例:\n")
|
||
|
||
var numbers [5]int // 声明长度为5的整数数组
|
||
fmt.Printf(" 声明数组: %v\n", numbers)
|
||
fmt.Printf(" 数组长度: %d\n", len(numbers))
|
||
fmt.Printf(" 数组类型: %T\n", numbers)
|
||
|
||
// 数组元素访问
|
||
numbers[0] = 10
|
||
numbers[1] = 20
|
||
numbers[2] = 30
|
||
fmt.Printf(" 修改元素后: %v\n", numbers)
|
||
fmt.Printf(" 第一个元素: %d\n", numbers[0])
|
||
fmt.Printf(" 最后一个元素: %d\n", numbers[len(numbers)-1])
|
||
|
||
fmt.Println()
|
||
}
|
||
|
||
// demonstrateArrayDeclaration 演示数组的声明和初始化
|
||
func demonstrateArrayDeclaration() {
|
||
fmt.Println("2. 数组的声明和初始化:")
|
||
|
||
// 方式1: 声明后赋值
|
||
fmt.Printf(" 方式1 - 声明后赋值:\n")
|
||
var arr1 [3]int
|
||
arr1[0] = 1
|
||
arr1[1] = 2
|
||
arr1[2] = 3
|
||
fmt.Printf(" arr1: %v\n", arr1)
|
||
|
||
// 方式2: 声明时初始化
|
||
fmt.Printf(" 方式2 - 声明时初始化:\n")
|
||
var arr2 = [3]int{10, 20, 30}
|
||
fmt.Printf(" arr2: %v\n", arr2)
|
||
|
||
// 方式3: 短变量声明
|
||
fmt.Printf(" 方式3 - 短变量声明:\n")
|
||
arr3 := [4]string{"Go", "Python", "Java", "C++"}
|
||
fmt.Printf(" arr3: %v\n", arr3)
|
||
|
||
// 方式4: 自动推断长度
|
||
fmt.Printf(" 方式4 - 自动推断长度:\n")
|
||
arr4 := [...]int{1, 2, 3, 4, 5} // ... 让编译器计算长度
|
||
fmt.Printf(" arr4: %v (长度: %d)\n", arr4, len(arr4))
|
||
|
||
// 方式5: 指定索引初始化
|
||
fmt.Printf(" 方式5 - 指定索引初始化:\n")
|
||
arr5 := [5]int{1: 10, 3: 30, 4: 40} // 指定索引1,3,4的值
|
||
fmt.Printf(" arr5: %v\n", arr5)
|
||
|
||
// 方式6: 混合初始化
|
||
fmt.Printf(" 方式6 - 混合初始化:\n")
|
||
arr6 := [...]string{0: "first", 2: "third", "fourth"} // 混合指定索引和顺序
|
||
fmt.Printf(" arr6: %v (长度: %d)\n", arr6, len(arr6))
|
||
|
||
// 不同类型的数组
|
||
fmt.Printf(" 不同类型的数组:\n")
|
||
boolArray := [3]bool{true, false, true}
|
||
floatArray := [4]float64{1.1, 2.2, 3.3, 4.4}
|
||
stringArray := [2]string{"hello", "world"}
|
||
|
||
fmt.Printf(" 布尔数组: %v\n", boolArray)
|
||
fmt.Printf(" 浮点数组: %v\n", floatArray)
|
||
fmt.Printf(" 字符串数组: %v\n", stringArray)
|
||
|
||
fmt.Println()
|
||
}
|
||
|
||
// demonstrateArrayOperations 演示数组的操作
|
||
func demonstrateArrayOperations() {
|
||
fmt.Println("3. 数组的操作:")
|
||
|
||
// 数组遍历
|
||
fmt.Printf(" 数组遍历:\n")
|
||
fruits := [4]string{"apple", "banana", "cherry", "date"}
|
||
|
||
// 使用索引遍历
|
||
fmt.Printf(" 使用索引遍历:\n")
|
||
for i := 0; i < len(fruits); i++ {
|
||
fmt.Printf(" fruits[%d] = %s\n", i, fruits[i])
|
||
}
|
||
|
||
// 使用 range 遍历
|
||
fmt.Printf(" 使用 range 遍历:\n")
|
||
for index, value := range fruits {
|
||
fmt.Printf(" fruits[%d] = %s\n", index, value)
|
||
}
|
||
|
||
// 只要值,忽略索引
|
||
fmt.Printf(" 只要值,忽略索引:\n")
|
||
for _, fruit := range fruits {
|
||
fmt.Printf(" 水果: %s\n", fruit)
|
||
}
|
||
|
||
// 只要索引,忽略值
|
||
fmt.Printf(" 只要索引,忽略值:\n")
|
||
for index := range fruits {
|
||
fmt.Printf(" 索引: %d\n", index)
|
||
}
|
||
|
||
// 数组修改
|
||
fmt.Printf(" 数组修改:\n")
|
||
numbers := [5]int{1, 2, 3, 4, 5}
|
||
fmt.Printf(" 原始数组: %v\n", numbers)
|
||
|
||
// 修改单个元素
|
||
numbers[2] = 99
|
||
fmt.Printf(" 修改索引2: %v\n", numbers)
|
||
|
||
// 批量修改
|
||
for i := range numbers {
|
||
numbers[i] *= 2
|
||
}
|
||
fmt.Printf(" 全部乘以2: %v\n", numbers)
|
||
|
||
// 数组查找
|
||
fmt.Printf(" 数组查找:\n")
|
||
target := "cherry"
|
||
found := false
|
||
foundIndex := -1
|
||
|
||
for i, fruit := range fruits {
|
||
if fruit == target {
|
||
found = true
|
||
foundIndex = i
|
||
break
|
||
}
|
||
}
|
||
|
||
if found {
|
||
fmt.Printf(" 找到 '%s' 在索引 %d\n", target, foundIndex)
|
||
} else {
|
||
fmt.Printf(" 未找到 '%s'\n", target)
|
||
}
|
||
|
||
fmt.Println()
|
||
}// de
|
||
monstrateMultiDimensionalArrays 演示多维数组
|
||
func demonstrateMultiDimensionalArrays() {
|
||
fmt.Println("4. 多维数组:")
|
||
|
||
// 二维数组
|
||
fmt.Printf(" 二维数组:\n")
|
||
|
||
// 声明和初始化二维数组
|
||
var matrix [3][4]int
|
||
fmt.Printf(" 声明 3x4 矩阵: %v\n", matrix)
|
||
|
||
// 初始化二维数组
|
||
matrix2 := [2][3]int{
|
||
{1, 2, 3},
|
||
{4, 5, 6},
|
||
}
|
||
fmt.Printf(" 初始化 2x3 矩阵:\n")
|
||
for i, row := range matrix2 {
|
||
fmt.Printf(" 行 %d: %v\n", i, row)
|
||
}
|
||
|
||
// 访问和修改二维数组元素
|
||
fmt.Printf(" 访问元素 matrix2[1][2]: %d\n", matrix2[1][2])
|
||
matrix2[0][1] = 99
|
||
fmt.Printf(" 修改后 matrix2[0][1]: %d\n", matrix2[0][1])
|
||
|
||
// 遍历二维数组
|
||
fmt.Printf(" 遍历二维数组:\n")
|
||
for i := 0; i < len(matrix2); i++ {
|
||
for j := 0; j < len(matrix2[i]); j++ {
|
||
fmt.Printf(" matrix2[%d][%d] = %d\n", i, j, matrix2[i][j])
|
||
}
|
||
}
|
||
|
||
// 使用 range 遍历二维数组
|
||
fmt.Printf(" 使用 range 遍历:\n")
|
||
for i, row := range matrix2 {
|
||
for j, value := range row {
|
||
fmt.Printf(" [%d][%d] = %d\n", i, j, value)
|
||
}
|
||
}
|
||
|
||
// 三维数组
|
||
fmt.Printf(" 三维数组:\n")
|
||
cube := [2][2][2]int{
|
||
{
|
||
{1, 2},
|
||
{3, 4},
|
||
},
|
||
{
|
||
{5, 6},
|
||
{7, 8},
|
||
},
|
||
}
|
||
|
||
fmt.Printf(" 3D 数组:\n")
|
||
for i, plane := range cube {
|
||
fmt.Printf(" 平面 %d:\n", i)
|
||
for j, row := range plane {
|
||
fmt.Printf(" 行 %d: %v\n", j, row)
|
||
}
|
||
}
|
||
|
||
// 实际应用:游戏棋盘
|
||
fmt.Printf(" 实际应用 - 井字棋棋盘:\n")
|
||
board := [3][3]string{
|
||
{"X", "O", "X"},
|
||
{"O", "X", "O"},
|
||
{"X", "O", "X"},
|
||
}
|
||
|
||
fmt.Printf(" 井字棋棋盘:\n")
|
||
for i, row := range board {
|
||
fmt.Printf(" ")
|
||
for j, cell := range row {
|
||
fmt.Printf("%s", cell)
|
||
if j < len(row)-1 {
|
||
fmt.Printf(" | ")
|
||
}
|
||
}
|
||
fmt.Printf("\n")
|
||
if i < len(board)-1 {
|
||
fmt.Printf(" ---------\n")
|
||
}
|
||
}
|
||
|
||
fmt.Println()
|
||
}
|
||
|
||
// demonstrateArrayProperties 演示数组的特性
|
||
func demonstrateArrayProperties() {
|
||
fmt.Println("5. 数组的特性:")
|
||
|
||
// 数组是值类型
|
||
fmt.Printf(" 数组是值类型:\n")
|
||
arr1 := [3]int{1, 2, 3}
|
||
arr2 := arr1 // 拷贝整个数组
|
||
|
||
fmt.Printf(" 原数组 arr1: %v\n", arr1)
|
||
fmt.Printf(" 拷贝数组 arr2: %v\n", arr2)
|
||
|
||
// 修改拷贝不影响原数组
|
||
arr2[0] = 99
|
||
fmt.Printf(" 修改 arr2[0] 后:\n")
|
||
fmt.Printf(" arr1: %v (未改变)\n", arr1)
|
||
fmt.Printf(" arr2: %v (已改变)\n", arr2)
|
||
|
||
// 数组比较
|
||
fmt.Printf(" 数组比较:\n")
|
||
a := [3]int{1, 2, 3}
|
||
b := [3]int{1, 2, 3}
|
||
c := [3]int{1, 2, 4}
|
||
|
||
fmt.Printf(" a == b: %t\n", a == b)
|
||
fmt.Printf(" a == c: %t\n", a == c)
|
||
fmt.Printf(" a != c: %t\n", a != c)
|
||
|
||
// 注意:不同长度的数组是不同类型,不能比较
|
||
// var d [4]int
|
||
// fmt.Println(a == d) // 编译错误
|
||
|
||
// 数组作为函数参数
|
||
fmt.Printf(" 数组作为函数参数:\n")
|
||
original := [3]int{10, 20, 30}
|
||
fmt.Printf(" 调用前: %v\n", original)
|
||
|
||
modifyArrayByValue(original)
|
||
fmt.Printf(" 值传递后: %v (未改变)\n", original)
|
||
|
||
modifyArrayByPointer(&original)
|
||
fmt.Printf(" 指针传递后: %v (已改变)\n", original)
|
||
|
||
// 数组的零值
|
||
fmt.Printf(" 数组的零值:\n")
|
||
var zeroIntArray [3]int
|
||
var zeroStringArray [2]string
|
||
var zeroBoolArray [2]bool
|
||
|
||
fmt.Printf(" int 数组零值: %v\n", zeroIntArray)
|
||
fmt.Printf(" string 数组零值: %v\n", zeroStringArray)
|
||
fmt.Printf(" bool 数组零值: %v\n", zeroBoolArray)
|
||
|
||
// 数组长度是编译时常量
|
||
fmt.Printf(" 数组长度是编译时常量:\n")
|
||
const size = 5
|
||
constArray := [size]int{1, 2, 3, 4, 5}
|
||
fmt.Printf(" 使用常量定义数组: %v\n", constArray)
|
||
|
||
fmt.Println()
|
||
}
|
||
|
||
// demonstratePracticalApplications 演示数组的实际应用
|
||
func demonstratePracticalApplications() {
|
||
fmt.Println("6. 数组的实际应用:")
|
||
|
||
// 应用1: 统计和分析
|
||
fmt.Printf(" 应用1 - 成绩统计:\n")
|
||
scores := [10]int{85, 92, 78, 96, 88, 73, 91, 87, 94, 82}
|
||
|
||
// 计算总分和平均分
|
||
total := 0
|
||
for _, score := range scores {
|
||
total += score
|
||
}
|
||
average := float64(total) / float64(len(scores))
|
||
|
||
// 找最高分和最低分
|
||
max, min := scores[0], scores[0]
|
||
for _, score := range scores {
|
||
if score > max {
|
||
max = score
|
||
}
|
||
if score < min {
|
||
min = score
|
||
}
|
||
}
|
||
|
||
fmt.Printf(" 成绩: %v\n", scores)
|
||
fmt.Printf(" 总分: %d\n", total)
|
||
fmt.Printf(" 平均分: %.2f\n", average)
|
||
fmt.Printf(" 最高分: %d\n", max)
|
||
fmt.Printf(" 最低分: %d\n", min)
|
||
|
||
// 应用2: 频率统计
|
||
fmt.Printf(" 应用2 - 字符频率统计:\n")
|
||
text := "hello world"
|
||
var frequency [26]int // 26个字母的频率
|
||
|
||
for _, char := range text {
|
||
if char >= 'a' && char <= 'z' {
|
||
frequency[char-'a']++
|
||
}
|
||
}
|
||
|
||
fmt.Printf(" 文本: \"%s\"\n", text)
|
||
fmt.Printf(" 字母频率:\n")
|
||
for i, count := range frequency {
|
||
if count > 0 {
|
||
fmt.Printf(" %c: %d\n", 'a'+i, count)
|
||
}
|
||
}
|
||
|
||
// 应用3: 查找表
|
||
fmt.Printf(" 应用3 - 月份天数查找表:\n")
|
||
daysInMonth := [12]int{31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
|
||
months := [12]string{
|
||
"一月", "二月", "三月", "四月", "五月", "六月",
|
||
"七月", "八月", "九月", "十月", "十一月", "十二月",
|
||
}
|
||
|
||
for i := 0; i < len(months); i++ {
|
||
fmt.Printf(" %s: %d天\n", months[i], daysInMonth[i])
|
||
}
|
||
|
||
// 应用4: 缓冲区
|
||
fmt.Printf(" 应用4 - 固定大小缓冲区:\n")
|
||
buffer := [8]byte{}
|
||
data := []byte("Hello")
|
||
|
||
// 将数据复制到缓冲区
|
||
copy(buffer[:], data)
|
||
fmt.Printf(" 缓冲区: %v\n", buffer)
|
||
fmt.Printf(" 缓冲区内容: %s\n", string(buffer[:len(data)]))
|
||
|
||
// 应用5: 矩阵运算
|
||
fmt.Printf(" 应用5 - 矩阵加法:\n")
|
||
matrixA := [2][2]int{{1, 2}, {3, 4}}
|
||
matrixB := [2][2]int{{5, 6}, {7, 8}}
|
||
var result [2][2]int
|
||
|
||
// 矩阵加法
|
||
for i := 0; i < 2; i++ {
|
||
for j := 0; j < 2; j++ {
|
||
result[i][j] = matrixA[i][j] + matrixB[i][j]
|
||
}
|
||
}
|
||
|
||
fmt.Printf(" 矩阵A: %v\n", matrixA)
|
||
fmt.Printf(" 矩阵B: %v\n", matrixB)
|
||
fmt.Printf(" A + B: %v\n", result)
|
||
|
||
fmt.Println()
|
||
}
|
||
|
||
// demonstrateArrayVsSlice 演示数组 vs 切片
|
||
func demonstrateArrayVsSlice() {
|
||
fmt.Println("7. 数组 vs 切片:")
|
||
|
||
fmt.Printf(" 数组特点:\n")
|
||
fmt.Printf(" - 固定长度,长度是类型的一部分\n")
|
||
fmt.Printf(" - 值类型,赋值和传参会拷贝\n")
|
||
fmt.Printf(" - 内存效率高,栈上分配\n")
|
||
fmt.Printf(" - 编译时确定大小\n")
|
||
fmt.Printf(" - 可以直接比较\n")
|
||
|
||
fmt.Printf(" 切片特点:\n")
|
||
fmt.Printf(" - 动态长度,可以增长和缩减\n")
|
||
fmt.Printf(" - 引用类型,赋值和传参传递引用\n")
|
||
fmt.Printf(" - 更灵活,但有额外开销\n")
|
||
fmt.Printf(" - 运行时动态分配\n")
|
||
fmt.Printf(" - 不能直接比较\n")
|
||
|
||
// 性能比较示例
|
||
fmt.Printf(" 使用场景建议:\n")
|
||
fmt.Printf(" 数组适用于:\n")
|
||
fmt.Printf(" - 固定大小的数据集合\n")
|
||
fmt.Printf(" - 性能敏感的场景\n")
|
||
fmt.Printf(" - 需要值语义的场景\n")
|
||
fmt.Printf(" - 小规模数据\n")
|
||
|
||
fmt.Printf(" 切片适用于:\n")
|
||
fmt.Printf(" - 动态大小的数据集合\n")
|
||
fmt.Printf(" - 需要灵活操作的场景\n")
|
||
fmt.Printf(" - 大规模数据处理\n")
|
||
fmt.Printf(" - 函数参数传递\n")
|
||
|
||
// 转换示例
|
||
fmt.Printf(" 数组和切片的转换:\n")
|
||
arr := [5]int{1, 2, 3, 4, 5}
|
||
slice := arr[:] // 数组转切片
|
||
|
||
fmt.Printf(" 原数组: %v\n", arr)
|
||
fmt.Printf(" 转换的切片: %v\n", slice)
|
||
|
||
// 修改切片会影响原数组
|
||
slice[0] = 99
|
||
fmt.Printf(" 修改切片后:\n")
|
||
fmt.Printf(" 数组: %v\n", arr)
|
||
fmt.Printf(" 切片: %v\n", slice)
|
||
|
||
fmt.Println()
|
||
}
|
||
|
||
// ========== 辅助函数 ==========
|
||
|
||
// 值传递数组(会拷贝整个数组)
|
||
func modifyArrayByValue(arr [3]int) {
|
||
arr[0] = 999
|
||
fmt.Printf(" 函数内修改: %v\n", arr)
|
||
}
|
||
|
||
// 指针传递数组(传递数组地址)
|
||
func modifyArrayByPointer(arr *[3]int) {
|
||
arr[0] = 888
|
||
fmt.Printf(" 函数内修改: %v\n", *arr)
|
||
}
|
||
|
||
/*
|
||
运行这个程序:
|
||
go run 01-arrays.go
|
||
|
||
学习要点:
|
||
1. 数组是固定长度的元素序列,长度是类型的一部分
|
||
2. 数组是值类型,赋值和传参会拷贝整个数组
|
||
3. 数组的零值是所有元素都为零值的数组
|
||
4. 数组可以直接比较(相同类型和长度)
|
||
5. 多维数组本质上是数组的数组
|
||
|
||
数组的特性:
|
||
1. 固定长度:编译时确定,不能改变
|
||
2. 类型安全:所有元素必须是相同类型
|
||
3. 连续内存:元素在内存中连续存储
|
||
4. 索引访问:O(1) 时间复杂度
|
||
5. 值语义:赋值和传参会拷贝
|
||
|
||
声明和初始化方式:
|
||
1. var arr [n]Type
|
||
2. arr := [n]Type{values...}
|
||
3. arr := [...]Type{values...} // 自动推断长度
|
||
4. arr := [n]Type{index: value} // 指定索引
|
||
|
||
数组 vs 切片:
|
||
1. 数组:固定长度,值类型,性能高
|
||
2. 切片:动态长度,引用类型,更灵活
|
||
|
||
使用建议:
|
||
1. 小规模、固定大小的数据使用数组
|
||
2. 需要动态调整大小时使用切片
|
||
3. 性能敏感的场景考虑数组
|
||
4. 函数参数通常使用切片而不是数组
|
||
|
||
常见应用场景:
|
||
1. 固定大小的缓冲区
|
||
2. 查找表和映射表
|
||
3. 矩阵和多维数据
|
||
4. 统计和频率分析
|
||
5. 游戏棋盘等固定结构
|
||
|
||
注意事项:
|
||
1. 数组长度是类型的一部分
|
||
2. 不同长度的数组是不同类型
|
||
3. 数组传参会拷贝,大数组考虑传指针
|
||
4. 数组索引越界会 panic
|
||
5. 多维数组的内存布局是行优先的
|
||
*/ |