探索 Go1.21 中的 slices 包:原理,特点和示例

阅读本文大概需要 6 分钟。

slices 标准库是 Go 1.21 新增的一个包,它提供了许多对切片(slices)进行常见操作的泛型函数,可以适用于任何元素类型的切片。切片是 Go 语言中一种重要的数据结构,它可以动态地存储和管理一组相同类型的元素。切片的底层实现是一个数组,但是切片可以根据需要自动地增长或缩小,而不需要手动分配或释放内存。切片的声明方式如下:

var s []int // 声明一个 int 类型的空切片
s = make([]int, 10) // 使用 make 函数创建一个长度为 10 的切片
s = []int{1, 2, 3} // 使用字面量初始化一个切片

slices 标准库为切片提供了以下几类操作:

  • 搜索:使用二分法在有序的切片中查找某个元素或满足某个条件的元素。

  • 压缩:将切片中连续相等的元素替换为单个元素,从而减少切片的长度。

  • 比较:比较两个切片是否相等或者按照字典序大小关系。

  • 包含:检查切片中是否存在某个元素或满足某个条件的元素。

  • 克隆:创建一个新的切片,复制原切片中的所有元素。

  • 索引:返回切片中某个元素或满足某个条件的元素的索引位置。

  • 插入:在切片中插入一个或多个元素,返回一个新的切片。

  • 排序:对切片中的元素进行排序,可以使用默认的比较函数或自定义的比较函数。

  • 最大值和最小值:返回切片中的最大值或最小值,可以使用默认的比较函数或自定义的比较函数。

  • 移除:从切片中移除一个或多个元素,返回一个新的切片。

  • 反转:将切片中的元素顺序反转,返回一个新的切片。

  • 替换:将切片中的一个或多个元素替换为另一个或多个元素,返回一个新的切片。

实现原理

slices 标准库使用了 Go 1.18 引入的泛型特性,使得它可以支持任何类型的切片。泛型是一种编程语言特性,它允许编写出可以适用于不同类型参数的函数或类型,而不需要重复编写代码。Go 1.18 的泛型特性主要包括以下几个方面:

  • 类型参数:使用方括号声明一个或多个类型参数。例如 func Print[T any](t ...T) 表示定义了一个泛型函数 Print,它接受任何类型 T 的切片作为参数。

  • 类型约束:使用关键字 constraints 定义一个类型约束,它表示一组允许的类型参数。例如 type Ordered interface { constraints.Ordered } 表示定义了一个类型约束 Ordered,它只允许实现了 constraints.Ordered 接口(即支持 <, <=, >, >= 操作符)的类型参数。类型约束可以用在函数或类型定义中,例如 func Max[T Ordered](t1, t2 T) 表示定义了一个泛型函数 Max,它接受两个 Ordered 类型参数,并返回其中较大者。

  • 泛型类型:使用类型参数定义一个泛型类型,例如 type Stack[T any] struct { data []T } 表示定义了一个泛型类型 Stack,它是一个栈结构,可以存储任何类型 T 的元素。

  • 泛型实例化:使用具体的类型参数实例化一个泛型函数或类型,例如 Print[int](s) 表示调用 Print 函数,并将 int 类型作为类型参数 T。如果类型参数可以从上下文推断出来,可以省略不写,例如 Print(s)

slices 标准库中的泛型函数大多使用了一个类型参数 E,表示切片的元素类型。例如 func BinarySearch[S ~[]E, E cmp.Ordered](x S, target E) (int, bool) 表示定义了一个泛型函数 BinarySearch,它接受一个有序的切片 x 和一个目标元素 target,都是 E 类型,并返回 target 在 x 中的索引位置和是否找到的布尔值。E 类型必须满足 Ordered 类型约束,即支持比较操作符。

slices 标准库中的泛型函数还使用了一些内置的泛型函数,例如 sort.Slice 和 copy,它们也可以适用于任何类型的切片。例如 sort.Slice(x, func(i, j int) bool { return x[i] < x[j] }) 表示对切片 x 进行排序,使用默认的比较函数。copy(dst, src) 表示将切片 src 中的元素复制到切片 dst 中。

应用场景和示例

slices 标准库可以在很多场景中使用,下面我们举一些例子来说明它的用法。

二分搜索

二分搜索是一种在有序的切片中查找某个元素或满足某个条件的元素的算法,它每次将搜索范围缩小一半,直到找到目标元素或搜索范围为空。slices 标准库提供了两个二分搜索的函数:

  • func BinarySearch[S ~[]E, E cmp.Ordered](x S, target E) (int, bool):在有序的切片 x 中查找目标元素 target,并返回其索引位置和是否找到的布尔值。

  • func BinarySearchFunc[S ~[]E, E, T any](x S, target T, cmp func(E, T) int) (int, bool):在有序的切片 x 中查找满足自定义比较函数 cmp 的元素,并返回其索引位置和是否找到的布尔值。cmp 函数接受两个参数 e 和 t,分别是切片 x 的元素和目标值 target,并返回一个整数表示比较结果。如果 e < t,则返回负数;如果 e == t,则返回零;如果 e > t,则返回正数。

下面是一个使用二分搜索的例子:

package main

import (
 "fmt"
 "slices"
)

func main() {
 // 创建一个有序的 int 切片
 x := []int{1, 2, 3, 4, 5, 6, 7, 8, 9}

 // 使用 BinarySearch 在 x 中查找 5
 i, found := slices.BinarySearch(x, 5)
 fmt.Printf("BinarySearch: found = %t, i = %d\n", found, i) // BinarySearch: found = true, i = 4

 // 使用 BinarySearch 在 x 中查找 10
 i, found = slices.BinarySearch(x, 10)
 fmt.Printf("BinarySearch: found = %t, i = %d\n", found, i) // BinarySearch: found = false, i = 9

 // 使用 BinarySearchFunc 在 x 中查找第一个大于等于 6 的元素
 i, found = slices.BinarySearchFunc(x, 6, func(e int, t int) int {
  if e >= t {
   return 0
  }
  return -1
 })
 fmt.Printf("BinarySearchFunc: found = %t, i = %d\n", found, i) // BinarySearchFunc: found = true, i = 5
}

Clip, Clone 和 Compact

Clip, Clone 和 Compact 是 slices 标准库中提供的三个用于压缩切片的函数,它们的作用和用法如下:

  • func Clip[S ~[]E, E any](s S) S:返回一个新的切片:s[:len(s):len(s)],从切片中删除未使用的容量。

  • func Clone[S ~[]E, E any](s S) S:返回一个新的切片,它包含了 s 中所有元素的副本。

  • func Compact[S ~[]E, E comparable](s S) S:返回一个新的切片,它将 s 中连续相等的元素替换为单个元素,从而减少切片的长度。E 类型必须满足 comparable 类型约束。

下面是一个使用 Clip, Clone 和 Compact 的例子:

package main

import (
 "fmt"
 "slices"
)

func main() {
 x := make([]string, 4, 6)
 x[0] = "a"
 x[1] = "a"
 x[2] = "b"
 x[3] = "c"

 // 使用 Clip 返回 x 中前 5 个元素
 y := slices.Clip(x)
 fmt.Println("Clip:", cap(y)) // Clip: 4

 // 使用 Clone 返回 x 的副本
 z := slices.Clone(x)
 fmt.Println("Clone:", z) // Clone: [a a b c]

 // 使用 Compact 压缩 x 中连续相等的元素
 w := slices.Compact(x)
 fmt.Println("Compact:", w) // Compact: [a b c]
}

slices 包中还有其他函数就不一一举例了。

slices 包可以满足常见的切片操作,大家可以升级到 Go1.21 版本使用。


往期推荐

我是 polarisxu,北大硕士毕业,曾在 360 等知名互联网公司工作,10多年技术研发与架构经验!2012 年接触 Go 语言并创建了 Go 语言中文网!著有《Go语言编程之旅》、开源图书《Go语言标准库》等。

坚持输出技术(包括 Go、Rust 等技术)、职场心得和创业感悟!欢迎关注「polarisxu」一起成长!也欢迎加我微信好友交流:gopherstudio

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值