Slice 类型 - Slice Types

← 返回数据类型

切片是动态长度的相同类型元素序列,是对数组的抽象。

切片基础

切片声明

// 声明切片(nil 切片)
var s1 []int
fmt.Println(s1 == nil)  // true
fmt.Println(len(s1))    // 0

// 使用字面量创建
s2 := []int{1, 2, 3}
fmt.Println(len(s2))  // 3

// 使用 make 创建
s3 := make([]int, 3)        // 长度为3,容量为3
s4 := make([]int, 3, 5)     // 长度为3,容量为5

切片操作

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

// 切片操作 [开始:结束](不包括结束)
fmt.Println(s[1:3])  // [2 3]
fmt.Println(s[1:])   // [2 3 4 5]
fmt.Println(s[:3])   // [1 2 3]
fmt.Println(s[:])    // [1 2 3 4 5]

// 追加元素
s = append(s, 6)
fmt.Println(s)  // [1 2 3 4 5 6]

// 追加多个元素
s = append(s, 7, 8, 9)
重要

切片的长度不能通过 nil 判断,必须使用 len(s) 来判断切片是否为空。

// ✅ 正确:使用 len() 判断
if len(s) == 0 {
    // 切片为空
}

切片内部结构

切片由三部分组成:

type slice struct {
    ptr unsafe.Pointer  // 指向底层数组的指针
    len int              // 切片长度
    cap int              // 切片容量
}

切片扩容机制

扩容策略

s := make([]int, 0, 3)
s = append(s, 1, 2, 3)
fmt.Printf("len=%d cap=%d\n", len(s), cap(s))  // len=3 cap=3

s = append(s, 4)  // 触发扩容
fmt.Printf("len=%d cap=%d\n", len(s), cap(s))  // len=4 cap=6

扩容流程

扩容规则
  • 容量 < 1024:新容量 = 旧容量 × 2
  • 容量 ≥ 1024:新容量 = 旧容量 × 1.25

切片陷阱

陷阱1:切片共享底层数组

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

s2[0] = 99
fmt.Println(s1)  // [1 99 3 4 5] - s1 也被修改了!
fmt.Println(s2)  // [99 3]

陷阱2:append 可能不影响原切片

s1 := []int{1, 2, 3}
s2 := s1[0:2]  // [1 2]

s2 = append(s2, 99)
fmt.Println(s1)  // [1 2 99] - 共享底层数组,被修改

// 但如果触发扩容...
s3 := s1[0:2]
s3 = append(s3, 100, 200)  // 触发扩容
fmt.Println(s1)  // [1 2 99] - 不受影响,s3 使用新数组

解决方案

// 使用 copy() 创建独立副本
s1 := []int{1, 2, 3, 4, 5}
s2 := make([]int, len(s1[1:3]))
copy(s2, s1[1:3])

s2[0] = 99
fmt.Println(s1)  // [1 2 3 4 5] - s1 不受影响
fmt.Println(s2)  // [99 3]

切片操作技巧

复制切片

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

// 方式1:使用 copy
s2 := make([]int, len(s1))
copy(s2, s1)

// 方式2:使用 append
s3 := append([]int(nil), s1...)

// 方式3:直接追加到空切片
s4 := append([]int{}, s1...)

删除元素

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

// 删除索引 i 的元素
i := 2
s = append(s[:i], s[i+1:]...)
fmt.Println(s)  // [1 2 4 5]

// 删除多个元素
s = append(s[:1], s[3:]...)
fmt.Println(s)  // [1 5]

插入元素

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

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

多维切片

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

// 动态添加行
matrix = append(matrix, []int{7, 8, 9})

// 访问元素
fmt.Println(matrix[0][1])  // 2

练习

  1. 实现切片去重函数
  2. 编写函数过滤切片中的偶数
  3. 实现切片旋转(将前k个元素移到末尾)

← 数组 | 映射 →