Go语言快速入门Go语言快速入门
首页
基础篇
进阶篇
高阶篇
实战篇
Go官方网站
编程指南
首页
基础篇
进阶篇
高阶篇
实战篇
Go官方网站
编程指南
  • 基础篇

    • 🚀 基础篇
    • 第1章 - 环境安装
    • 第2章 - Hello World
    • 第3章 - 变量与常量
    • 第4章 - 数据类型
    • 控制流程
    • 函数
    • 数组与切片
    • Map
    • 结构体
    • 指针

数组与切片

数组和切片是Go中存储数据集合的方式。切片是Go中最常用的数据结构之一!说实话,我一开始对数组和切片的区别很困惑,经常搞混。后来才明白,在Go中,几乎99%的场景都用切片,数组很少用到。

数组

数组是固定长度的同类型元素集合。

声明和初始化

// 声明数组(零值初始化)
var arr1 [5]int  // [0 0 0 0 0]

// 声明并初始化
var arr2 = [5]int{1, 2, 3, 4, 5}

// 简短声明
arr3 := [5]int{1, 2, 3, 4, 5}

// 自动推断长度
arr4 := [...]int{1, 2, 3, 4, 5}  // 长度为5

// 指定索引初始化
arr5 := [5]int{0: 1, 4: 5}  // [1 0 0 0 5]

基本操作

arr := [5]int{1, 2, 3, 4, 5}

// 访问元素
fmt.Println(arr[0])  // 1
fmt.Println(arr[4])  // 5

// 修改元素
arr[0] = 10

// 获取长度
fmt.Println(len(arr))  // 5

// 遍历
for i, v := range arr {
    fmt.Printf("arr[%d] = %d\n", i, v)
}

数组是值类型!

这是Go数组的重要特点:

arr1 := [3]int{1, 2, 3}
arr2 := arr1  // 复制整个数组!
arr2[0] = 100

fmt.Println(arr1)  // [1 2 3](原数组不变)
fmt.Println(arr2)  // [100 2 3]

和Java/JavaScript的区别

// Java - 数组是引用类型
int[] arr1 = {1, 2, 3};
int[] arr2 = arr1;  // 引用同一个数组
arr2[0] = 100;      // arr1也变了!

Go的数组是值类型,赋值会复制整个数组。要传引用用切片!

切片(Slice)

切片是动态长度的数组,底层还是数组,但更灵活。

创建切片

// 从数组创建
arr := [5]int{1, 2, 3, 4, 5}
s1 := arr[1:4]  // [2 3 4](左闭右开)

// 直接声明
var s2 []int  // nil切片

// 字面量创建
s3 := []int{1, 2, 3, 4, 5}

// make创建(推荐!)
s4 := make([]int, 5)     // 长度5,容量5
s5 := make([]int, 3, 10) // 长度3,容量10

切片操作

s := []int{1, 2, 3, 4, 5}

// 长度和容量
fmt.Println(len(s))  // 5(长度)
fmt.Println(cap(s))  // 5(容量)

// 切片语法 [start:end]
s[1:4]   // [2 3 4]
s[:3]    // [1 2 3](从头开始)
s[2:]    // [3 4 5](到末尾)
s[:]     // [1 2 3 4 5](全部)

append - 追加元素

s := []int{1, 2, 3}

// 追加单个元素
s = append(s, 4)      // [1 2 3 4]

// 追加多个元素
s = append(s, 5, 6)   // [1 2 3 4 5 6]

// 追加切片
s2 := []int{7, 8, 9}
s = append(s, s2...)  // [1 2 3 4 5 6 7 8 9]

重要!

append 返回新切片,必须接收返回值!

s := []int{1, 2, 3}
append(s, 4)     // ❌ 错误用法,丢弃了返回值
s = append(s, 4) // ✅ 正确

copy - 复制切片

src := []int{1, 2, 3, 4, 5}
dst := make([]int, 3)

n := copy(dst, src)  // 复制3个元素
fmt.Println(dst)     // [1 2 3]
fmt.Println(n)       // 3(实际复制的数量)

切片是引用类型

arr := [5]int{1, 2, 3, 4, 5}
s1 := arr[1:4]  // [2 3 4]
s2 := s1

s2[0] = 100
fmt.Println(arr)  // [1 100 3 4 5](原数组也变了!)
fmt.Println(s1)   // [100 3 4]
fmt.Println(s2)   // [100 3 4]

切片的内部结构

切片本质上是一个结构体:

type slice struct {
    ptr *array   // 指向底层数组
    len int      // 长度
    cap int      // 容量
}

理解这个对于理解切片行为很重要!

常见操作

删除元素

s := []int{1, 2, 3, 4, 5}

// 删除索引为2的元素(值为3)
s = append(s[:2], s[3:]...)  // [1 2 4 5]

在中间插入

s := []int{1, 2, 4, 5}

// 在索引2处插入3
s = append(s[:2], append([]int{3}, s[2:]...)...)  // [1 2 3 4 5]

去重

func unique(s []int) []int {
    seen := make(map[int]bool)
    result := []int{}
    for _, v := range s {
        if !seen[v] {
            seen[v] = true
            result = append(result, v)
        }
    }
    return result
}

s := []int{1, 2, 2, 3, 3, 3, 4}
fmt.Println(unique(s))  // [1 2 3 4]

反转

func reverse(s []int) {
    for i, j := 0, len(s)-1; i < j; i, j = i+1, j-1 {
        s[i], s[j] = s[j], s[i]
    }
}

s := []int{1, 2, 3, 4, 5}
reverse(s)
fmt.Println(s)  // [5 4 3 2 1]

过滤

func filter(s []int, fn func(int) bool) []int {
    result := []int{}
    for _, v := range s {
        if fn(v) {
            result = append(result, v)
        }
    }
    return result
}

nums := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}

// 过滤出偶数
evens := filter(nums, func(n int) bool {
    return n%2 == 0
})
fmt.Println(evens)  // [2 4 6 8 10]

多维切片

// 二维切片
matrix := [][]int{
    {1, 2, 3},
    {4, 5, 6},
    {7, 8, 9},
}

// 访问
fmt.Println(matrix[1][2])  // 6

// 遍历
for i, row := range matrix {
    for j, val := range row {
        fmt.Printf("matrix[%d][%d] = %d\n", i, j, val)
    }
}

实战案例:学生成绩管理

package main

import (
    "fmt"
    "sort"
)

type Student struct {
    Name  string
    Score float64
}

func main() {
    // 学生列表
    students := []Student{
        {"张三", 85.5},
        {"李四", 92.0},
        {"王五", 78.5},
        {"赵六", 92.0},
        {"钱七", 65.0},
    }
    
    // 计算平均分
    var total float64
    for _, s := range students {
        total += s.Score
    }
    avg := total / float64(len(students))
    fmt.Printf("平均分: %.2f\n", avg)
    
    // 按成绩排序(降序)
    sort.Slice(students, func(i, j int) bool {
        return students[i].Score > students[j].Score
    })
    
    // 打印排名
    fmt.Println("\n成绩排名:")
    for i, s := range students {
        fmt.Printf("%d. %s: %.1f分\n", i+1, s.Name, s.Score)
    }
    
    // 筛选优秀学生(>=90分)
    excellent := []Student{}
    for _, s := range students {
        if s.Score >= 90 {
            excellent = append(excellent, s)
        }
    }
    
    fmt.Printf("\n优秀学生(%d人):\n", len(excellent))
    for _, s := range excellent {
        fmt.Printf("- %s: %.1f分\n", s.Name, s.Score)
    }
}

输出:

平均分: 82.60

成绩排名:
1. 李四: 92.0分
2. 赵六: 92.0分
3. 张三: 85.5分
4. 王五: 78.5分
5. 钱七: 65.0分

优秀学生(2人):
- 李四: 92.0分
- 赵六: 92.0分

切片 vs 数组:何时用哪个?

场景推荐原因
长度固定且已知数组性能略好
长度动态变化切片灵活
函数参数传递切片避免大量复制
99%的情况切片更常用、更灵活

练习

  1. 创建一个包含10个数字的切片,计算它们的和
  2. 实现一个函数,将切片中的所有负数变成0
  3. 实现一个函数,找出切片中的最大值和最小值
参考答案
package main

import (
    "fmt"
    "math"
)

// 1. 计算和
func sum(nums []int) int {
    total := 0
    for _, n := range nums {
        total += n
    }
    return total
}

// 2. 负数变0
func zeroNegatives(nums []int) {
    for i := range nums {
        if nums[i] < 0 {
            nums[i] = 0
        }
    }
}

// 3. 最大最小值
func minMax(nums []int) (min, max int) {
    if len(nums) == 0 {
        return 0, 0
    }
    min, max = math.MaxInt, math.MinInt
    for _, n := range nums {
        if n < min {
            min = n
        }
        if n > max {
            max = n
        }
    }
    return
}

func main() {
    // 1
    nums := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
    fmt.Printf("和: %d\n", sum(nums))  // 55
    
    // 2
    nums2 := []int{-3, -1, 0, 1, 3, 5}
    zeroNegatives(nums2)
    fmt.Printf("负数变0: %v\n", nums2)  // [0 0 0 1 3 5]
    
    // 3
    nums3 := []int{5, 2, 9, 1, 7, 3}
    min, max := minMax(nums3)
    fmt.Printf("最小: %d, 最大: %d\n", min, max)  // 最小: 1, 最大: 9
}

切片是Go中最常用的数据结构,下一节学习另一个重要的数据结构 Map!

最近更新: 2025/12/27 13:26
Contributors: 王长安
Prev
函数
Next
Map