Skip to content

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 语言的内存管理和引用机制。

数组使用建议

  • 数组长度在编译时确定,适用于固定大小的数据
  • 大数组作为函数参数时考虑使用指针避免复制开销
  • 实际开发中更多使用切片而非数组
  • 利用数组的值类型特性进行安全的数据传递

本站内容仅供学习和研究使用。