目录

  1. 切片的基本结构
  2. 切片的创建
  3. 切片的操作
  4. 切片的容量
  5. 切片的引用特性
  6. 切片的常见问题
  7. 总结

切片的基本结构

Go 语言中的切片(Slice)是对数组的一个动态抽象,它是一个更灵活的结构,能进行动态大小的调整。切片与数组的不同之处在于它的大小不固定,并且可以根据需求进行扩展。切片包含三个主要的部分:

  • 指针:指向切片数据的起始位置。
  • 长度len):切片当前包含的元素个数。
  • 容量cap):从切片的起始位置到底层数组的结尾的元素个数。

切片的创建

使用 make 函数创建切片

slice := make([]int, 5)  // 创建一个长度为 5 的切片,元素类型为 int,默认值为 0

make([]T, len, cap) 用来创建一个类型为 T,长度为 len,容量为 cap 的切片。如果 cap 被省略,则容量与长度相同。

通过数组创建切片

arr := [5]int{1, 2, 3, 4, 5}
slice := arr[1:4]  // 创建一个切片,包含 arr[1], arr[2], arr[3],即 [2, 3, 4]

arr[start:end] 通过指定开始和结束索引来创建切片(end 索引是不包含的)。

使用字面量创建切片

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

字面量创建并初始化一个切片。

切片的操作

切片的扩展

切片可以通过 append() 函数来扩展:

slice := []int{1, 2, 3}
slice = append(slice, 4, 5)  // 添加元素 4 和 5,slice 变为 [1, 2, 3, 4, 5]

append() 函数会返回一个新的切片。如果原切片的容量不够,Go 会为切片分配一个新的底层数组。

切片的删除

删除切片中的元素可以通过切片重组的方式来实现:

slice := []int{1, 2, 3, 4, 5}
slice = append(slice[:2], slice[3:]...)  // 删除索引 2 的元素,结果为 [1, 2, 4, 5]

使用 ... 展开切片。

取切片的子集

slice := []int{1, 2, 3, 4, 5}
subslice := slice[1:4]  // [2, 3, 4]

切片从原始切片中提取子切片。

切片的容量

切片的容量决定了它能够容纳多少元素。可以通过 cap() 函数查看切片的容量:

slice := make([]int, 5, 10)  // 创建长度为 5,容量为 10 的切片
fmt.Println(len(slice))  // 输出 5
fmt.Println(cap(slice))  // 输出 10

切片的引用特性

切片是引用类型,切片赋值不会复制底层数组,而是会共享底层数组的指针。这意味着修改一个切片会影响到其他共享相同底层数组的切片。

slice1 := []int{1, 2, 3}
slice2 := slice1
slice2[0] = 100
fmt.Println(slice1)  // [100, 2, 3]
fmt.Println(slice2)  // [100, 2, 3]

切片的常见问题

切片扩展时的内存管理

每当切片的容量不足时,Go 会创建一个新的更大的切片(通常是原容量的两倍),并将元素从旧切片复制到新切片中。这个过程会消耗性能,特别是在频繁扩展切片时。

共享底层数组

多个切片可以共享同一个底层数组,在这种情况下,修改一个切片会影响到其他切片。这在并发环境下可能引发数据竞争问题,因此需要小心处理。

slice1 := []int{1, 2, 3}
slice2 := slice1[:]
slice1[0] = 10
fmt.Println(slice1)  // [10, 2, 3]
fmt.Println(slice2)  // [10, 2, 3]

总结

Go 语言中的切片(Slice)提供了一种非常强大且灵活的数据结构,允许开发者进行动态数组操作。切片的引用特性、扩展能力以及简便的子切片操作,使得切片成为 Go 语言中最常用的数据类型之一。

参考资料

  1. Go 官方文档 – 切片
  2. Go 语言切片详解
  3. Go 切片和数组的区别