Go 语言数组
数组是相同类型元素的固定长度序列。在 Go 语言中,数组是值类型,长度是类型的一部分。理解数组对于掌握 Go 语言的数据结构至关重要。
📋 数组基础
数组声明和初始化
go
package main
import "fmt"
func main() {
// 1. 声明数组(零值初始化)
var arr1 [5]int // 声明长度为5的整数数组,所有元素为0
fmt.Printf("零值数组: %v\n", arr1)
// 2. 声明时初始化
var arr2 [5]int = [5]int{1, 2, 3, 4, 5}
fmt.Printf("初始化数组: %v\n", arr2)
// 3. 简化声明
arr3 := [5]int{10, 20, 30, 40, 50}
fmt.Printf("简化声明: %v\n", arr3)
// 4. 部分初始化(其余为零值)
arr4 := [5]int{1, 2} // 后三个元素为0
fmt.Printf("部分初始化: %v\n", arr4)
// 5. 指定索引初始化
arr5 := [5]int{0: 100, 2: 200, 4: 400}
fmt.Printf("指定索引初始化: %v\n", arr5)
// 6. 自动推断长度
arr6 := [...]int{1, 2, 3, 4, 5, 6}
fmt.Printf("自动推断长度: %v (长度: %d)\n", arr6, len(arr6))
}数组访问和修改
go
func main() {
fruits := [4]string{"苹果", "香蕉", "橙子", "葡萄"}
// 访问数组元素
fmt.Printf("第一个水果: %s\n", fruits[0])
fmt.Printf("最后一个水果: %s\n", fruits[len(fruits)-1])
// 修改数组元素
fruits[1] = "草莓"
fmt.Printf("修改后的数组: %v\n", fruits)
// 数组长度
fmt.Printf("数组长度: %d\n", len(fruits))
// 遍历数组
fmt.Println("使用索引遍历:")
for i := 0; i < len(fruits); i++ {
fmt.Printf("索引 %d: %s\n", i, fruits[i])
}
fmt.Println("使用 range 遍历:")
for index, fruit := range fruits {
fmt.Printf("索引 %d: %s\n", index, fruit)
}
fmt.Println("只遍历值:")
for _, fruit := range fruits {
fmt.Printf("水果: %s\n", fruit)
}
}🔢 多维数组
二维数组
go
func main() {
// 二维数组声明和初始化
var matrix1 [3][4]int // 3行4列的二维数组
// 初始化二维数组
matrix2 := [3][4]int{
{1, 2, 3, 4},
{5, 6, 7, 8},
{9, 10, 11, 12},
}
fmt.Println("二维数组:")
for i := 0; i < len(matrix2); i++ {
for j := 0; j < len(matrix2[i]); j++ {
fmt.Printf("%3d ", matrix2[i][j])
}
fmt.Println()
}
// 使用 range 遍历二维数组
fmt.Println("\n使用 range 遍历:")
for i, row := range matrix2 {
fmt.Printf("第%d行: ", i)
for j, value := range row {
fmt.Printf("列%d=%d ", j, value)
}
fmt.Println()
}
// 修改二维数组元素
matrix2[1][2] = 100
fmt.Printf("\n修改后的矩阵[1][2]: %v\n", matrix2)
}三维数组和更高维
go
func main() {
// 三维数组:2个3x3的矩阵
cube := [2][3][3]int{
{
{1, 2, 3},
{4, 5, 6},
{7, 8, 9},
},
{
{10, 11, 12},
{13, 14, 15},
{16, 17, 18},
},
}
fmt.Println("三维数组:")
for i, matrix := range cube {
fmt.Printf("矩阵 %d:\n", i)
for j, row := range matrix {
fmt.Printf(" 行 %d: ", j)
for k, value := range row {
fmt.Printf("%2d ", value)
}
fmt.Println()
}
fmt.Println()
}
// 访问三维数组元素
fmt.Printf("cube[1][2][1] = %d\n", cube[1][2][1])
}🔄 数组操作
数组复制和比较
go
func main() {
// 数组是值类型,赋值时会复制整个数组
arr1 := [5]int{1, 2, 3, 4, 5}
arr2 := arr1 // 复制数组
fmt.Printf("原数组 arr1: %v\n", arr1)
fmt.Printf("复制数组 arr2: %v\n", arr2)
// 修改复制的数组不会影响原数组
arr2[0] = 100
fmt.Printf("修改 arr2 后:\n")
fmt.Printf("arr1: %v\n", arr1) // 不变
fmt.Printf("arr2: %v\n", arr2) // 改变
// 数组比较(相同类型和长度的数组可以直接比较)
arr3 := [5]int{1, 2, 3, 4, 5}
arr4 := [5]int{1, 2, 3, 4, 5}
arr5 := [5]int{1, 2, 3, 4, 6}
fmt.Printf("arr3 == arr4: %t\n", arr3 == arr4) // true
fmt.Printf("arr3 == arr5: %t\n", arr3 == arr5) // false
// 不同长度的数组类型不同,无法比较
// arr6 := [4]int{1, 2, 3, 4}
// fmt.Println(arr3 == arr6) // 编译错误
}数组作为函数参数
go
func main() {
scores := [5]int{85, 92, 78, 96, 88}
fmt.Printf("原始数组: %v\n", scores)
// 传递数组到函数(值传递)
total := sumArray(scores)
fmt.Printf("数组总和: %d\n", total)
fmt.Printf("函数调用后数组: %v\n", scores) // 数组不变
// 传递数组指针(引用传递)
doubleArrayValues(&scores)
fmt.Printf("翻倍后数组: %v\n", scores) // 数组改变
// 查找最大值和最小值
max, min := findMaxMin(scores)
fmt.Printf("最大值: %d, 最小值: %d\n", max, min)
}
// 值传递:计算数组总和
func sumArray(arr [5]int) int {
total := 0
for _, value := range arr {
total += value
}
return total
}
// 指针传递:将数组每个元素翻倍
func doubleArrayValues(arr *[5]int) {
for i := range arr {
arr[i] *= 2
}
}
// 查找数组中的最大值和最小值
func findMaxMin(arr [5]int) (int, int) {
max, min := arr[0], arr[0]
for _, value := range arr {
if value > max {
max = value
}
if value < min {
min = value
}
}
return max, min
}🎯 实际应用示例
学生成绩管理系统
go
func main() {
// 学生成绩数据:5个学生,4门课程
studentNames := [5]string{"Alice", "Bob", "Carol", "David", "Eve"}
subjectNames := [4]string{"数学", "物理", "化学", "英语"}
// 成绩矩阵:学生x科目
grades := [5][4]int{
{85, 92, 78, 88}, // Alice
{76, 84, 82, 90}, // Bob
{95, 89, 93, 96}, // Carol
{72, 78, 75, 85}, // David
{88, 91, 85, 92}, // Eve
}
fmt.Println("=== 学生成绩管理系统 ===")
// 显示成绩表
printGradeTable(studentNames, subjectNames, grades)
// 计算每个学生的平均分
fmt.Println("\n学生平均分:")
for i, name := range studentNames {
avg := calculateStudentAverage(grades[i])
fmt.Printf("%s: %.2f\n", name, avg)
}
// 计算每门课的平均分
fmt.Println("\n科目平均分:")
for j, subject := range subjectNames {
avg := calculateSubjectAverage(grades, j)
fmt.Printf("%s: %.2f\n", subject, avg)
}
// 找出最高分学生
bestStudent, bestScore := findBestStudent(studentNames, grades)
fmt.Printf("\n最高平均分学生: %s (%.2f分)\n", bestStudent, bestScore)
// 分析成绩分布
analyzeGradeDistribution(grades)
}
func printGradeTable(students [5]string, subjects [4]string, grades [5][4]int) {
fmt.Printf("%-8s", "学生")
for _, subject := range subjects {
fmt.Printf("%-6s", subject)
}
fmt.Println("平均分")
for i, student := range students {
fmt.Printf("%-8s", student)
total := 0
for j, grade := range grades[i] {
fmt.Printf("%-6d", grade)
total += grade
}
average := float64(total) / float64(len(grades[i]))
fmt.Printf("%.2f\n", average)
}
}
func calculateStudentAverage(studentGrades [4]int) float64 {
total := 0
for _, grade := range studentGrades {
total += grade
}
return float64(total) / float64(len(studentGrades))
}
func calculateSubjectAverage(allGrades [5][4]int, subjectIndex int) float64 {
total := 0
for i := 0; i < len(allGrades); i++ {
total += allGrades[i][subjectIndex]
}
return float64(total) / float64(len(allGrades))
}
func findBestStudent(students [5]string, grades [5][4]int) (string, float64) {
bestIndex := 0
bestAverage := calculateStudentAverage(grades[0])
for i := 1; i < len(students); i++ {
avg := calculateStudentAverage(grades[i])
if avg > bestAverage {
bestAverage = avg
bestIndex = i
}
}
return students[bestIndex], bestAverage
}
func analyzeGradeDistribution(grades [5][4]int) {
fmt.Println("\n成绩分布分析:")
// 统计各分数段的人次
ranges := [4][2]int{{90, 100}, {80, 89}, {70, 79}, {60, 69}}
rangeNames := [4]string{"优秀(90-100)", "良好(80-89)", "中等(70-79)", "及格(60-69)"}
rangeCounts := [4]int{}
totalScores := 0
for i := 0; i < len(grades); i++ {
for j := 0; j < len(grades[i]); j++ {
score := grades[i][j]
totalScores++
for k, r := range ranges {
if score >= r[0] && score <= r[1] {
rangeCounts[k]++
break
}
}
}
}
for i, name := range rangeNames {
percentage := float64(rangeCounts[i]) / float64(totalScores) * 100
fmt.Printf("%s: %d人次 (%.1f%%)\n", name, rangeCounts[i], percentage)
}
}矩阵运算
go
func main() {
// 3x3 矩阵
matrix1 := [3][3]int{
{1, 2, 3},
{4, 5, 6},
{7, 8, 9},
}
matrix2 := [3][3]int{
{9, 8, 7},
{6, 5, 4},
{3, 2, 1},
}
fmt.Println("矩阵1:")
printMatrix(matrix1)
fmt.Println("\n矩阵2:")
printMatrix(matrix2)
// 矩阵加法
sum := addMatrices(matrix1, matrix2)
fmt.Println("\n矩阵1 + 矩阵2:")
printMatrix(sum)
// 矩阵转置
transposed := transposeMatrix(matrix1)
fmt.Println("\n矩阵1的转置:")
printMatrix(transposed)
// 矩阵乘法
product := multiplyMatrices(matrix1, matrix2)
fmt.Println("\n矩阵1 × 矩阵2:")
printMatrix(product)
}
func printMatrix(matrix [3][3]int) {
for _, row := range matrix {
for _, value := range row {
fmt.Printf("%4d", value)
}
fmt.Println()
}
}
func addMatrices(a, b [3][3]int) [3][3]int {
var result [3][3]int
for i := 0; i < 3; i++ {
for j := 0; j < 3; j++ {
result[i][j] = a[i][j] + b[i][j]
}
}
return result
}
func transposeMatrix(matrix [3][3]int) [3][3]int {
var result [3][3]int
for i := 0; i < 3; i++ {
for j := 0; j < 3; j++ {
result[j][i] = matrix[i][j]
}
}
return result
}
func multiplyMatrices(a, b [3][3]int) [3][3]int {
var result [3][3]int
for i := 0; i < 3; i++ {
for j := 0; j < 3; j++ {
for k := 0; k < 3; k++ {
result[i][j] += a[i][k] * b[k][j]
}
}
}
return result
}📊 数组 vs 切片
对比分析
go
import "unsafe"
func main() {
// 数组 vs 切片的区别
// 1. 数组(固定长度)
arr := [5]int{1, 2, 3, 4, 5}
fmt.Printf("数组: %v\n", arr)
fmt.Printf("数组类型: %T\n", arr)
fmt.Printf("数组长度: %d\n", len(arr))
fmt.Printf("数组大小: %d 字节\n", unsafe.Sizeof(arr))
// 2. 切片(动态长度)
slice := []int{1, 2, 3, 4, 5}
fmt.Printf("\n切片: %v\n", slice)
fmt.Printf("切片类型: %T\n", slice)
fmt.Printf("切片长度: %d\n", len(slice))
fmt.Printf("切片容量: %d\n", cap(slice))
fmt.Printf("切片大小: %d 字节\n", unsafe.Sizeof(slice))
// 3. 数组到切片的转换
arrSlice := arr[:] // 基于数组创建切片
fmt.Printf("\n基于数组的切片: %v\n", arrSlice)
// 修改切片会影响原数组
arrSlice[0] = 100
fmt.Printf("修改切片后的数组: %v\n", arr)
fmt.Printf("修改切片后的切片: %v\n", arrSlice)
// 4. 性能对比
comparePerformance()
}
func comparePerformance() {
fmt.Println("\n=== 性能对比 ===")
// 大数组传递(值传递,复制整个数组)
largeArray := [10000]int{}
for i := range largeArray {
largeArray[i] = i
}
fmt.Printf("大数组大小: %d 字节\n", unsafe.Sizeof(largeArray))
// 计算数组传递的开销
start := getCurrentTime()
processArray(largeArray)
arrayTime := getCurrentTime() - start
// 计算切片传递的开销
largeSlice := largeArray[:]
start = getCurrentTime()
processSlice(largeSlice)
sliceTime := getCurrentTime() - start
fmt.Printf("数组传递耗时: %d 纳秒\n", arrayTime)
fmt.Printf("切片传递耗时: %d 纳秒\n", sliceTime)
fmt.Printf("性能差异: %.2fx\n", float64(arrayTime)/float64(sliceTime))
}
func getCurrentTime() int64 {
// 简化的时间获取,实际应用中使用 time.Now()
return 0
}
func processArray(arr [10000]int) int {
sum := 0
for _, v := range arr {
sum += v
}
return sum
}
func processSlice(slice []int) int {
sum := 0
for _, v := range slice {
sum += v
}
return sum
}🎓 小结
本章我们全面学习了 Go 语言的数组:
- ✅ 数组基础:声明、初始化、访问、修改
- ✅ 多维数组:二维数组、三维数组的使用
- ✅ 数组操作:复制、比较、函数参数传递
- ✅ 实际应用:成绩管理系统、矩阵运算
- ✅ 性能考虑:数组 vs 切片的性能对比
数组是 Go 语言的基础数据结构,虽然在实际开发中切片使用更频繁,但理解数组对于掌握 Go 语言的内存模型和性能特性很重要。
接下来,我们将学习 Go 语言指针,了解 Go 语言的内存管理和引用机制。
数组使用建议
- 数组长度在编译时确定,适用于固定大小的数据
- 大数组作为函数参数时考虑使用指针避免复制开销
- 实际开发中更多使用切片而非数组
- 利用数组的值类型特性进行安全的数据传递