前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Go 1.22 标准库 slices 新增函数和一些旧函数增加新特性

Go 1.22 标准库 slices 新增函数和一些旧函数增加新特性

作者头像
frank.
发布2024-04-02 16:51:17
990
发布2024-04-02 16:51:17
举报

01

介绍

Go 1.21 标准库中新增的 slices 提供了很多方便处理 slice 的函数。

Go 1.22 标准库 slices 引入一些新特性,其中包括新增函数 Concat、优化函数 DeleteDeleteFuncCompactCompactFuncReplaceInsert

本文我们介绍 Go 1.22 标准库 slices 的新增函数和优化旧函数的新特性,关于它们原有的一些特性,本文不再赘述。

02

新增函数 Concat

新函数 Concat 连接多个切片,返回一个连接多个切片的切片。

示例代码:

代码语言:javascript
复制
func main() {
 s1 := []int{1, 2, 3}
 s2 := []int{4, 5, 6}
 s3 := []int{7, 8, 9}
 s := slices.Concat(s1, s2, s3)
 fmt.Println(s)
}

输出结果:

代码语言:javascript
复制
[1 2 3 4 5 6 7 8 9]

阅读上面这段代码,我们定义 3 个切片类型的变量 s1s2s3,使用 go 1.22 标准库 slices 新增函数 Concat 拼接为 1 个切片。

在此之前,我们想要拼接 3 个切片为一个切片,需要使用 append 内置函数。

示例代码:

代码语言:javascript
复制
func main() {
 s1 := []int{1, 2, 3}
 s2 := []int{4, 5, 6}
 s3 := []int{7, 8, 9}
 //s := slices.Concat(s1, s2, s3)
 var s []int
 s = append(s, s1...)
 s = append(s, s2...)
 s = append(s, s3...)
 fmt.Println(s)
}

输出结果:

代码语言:javascript
复制
[1 2 3 4 5 6 7 8 9]

阅读上面这段代码,我们使用内置函数 append 拼接 3 个切片为 1 个切片,需要先定义 1 个切片类型的新变量 s,然后使用内置函数 append 每次追加 1 个切片类型的变量到变量 s,实现方式比较繁琐。

源码实现:

代码语言:javascript
复制
// Concat returns a new slice concatenating the passed in slices.
func Concat[S ~[]E, E any](slices ...S) S {
 size := 0
 for _, s := range slices {
  size += len(s)
  if size < 0 {
   panic("len out of range")
  }
 }
 newslice := Grow[S](nil, size)
 for _, s := range slices {
  newslice = append(newslice, s...)
 }
 return newslice
}

阅读源码,我们可以发现,func Concat[S ~[]E, E any](slices ...S) S 函数的实现比较简单,并且是基于泛型实现,不需要每个类型都实现一个对应的函数。

需要注意的是,它在拼接多个切片之前,先计算新切片的长度,然后使用 Grow 函数创建一个新切片,作为内置函数 append 的参数,这样可以避免内存分配。

为了避免触发 Grow 函数的 panic,它先对 Grow 函数的参数 size 进行了小于 0 的判断,感兴趣的读者朋友可以阅读 Grow 函数的源码。

03

元素归零优化

Go 1.22 优化缩小切片大小的函数,将新长度和旧长度之间的元素归零,包括函数 DeleteDeleteFuncCompactCompactFuncReplace

我们通过示例代码,分别在 Go 1.22 和 Go 1.21 中运行,查看运行结果的变化。

Delete

标准库 slices 的函数 func Delete[S ~[]E, E any](s S, i, j int) S 删除切片 ss[i:j] 中的元素,返回修改后的切片。

示例代码:

代码语言:javascript
复制
func main() {
 s1 := []int{1, 2, 3, 4, 5, 6, 7, 8, 9}
 s2 := slices.Delete(s1, 2, 5)
 fmt.Printf("len=%d\tcap=%d\tval=%d\n", len(s1), cap(s1), s1)
 fmt.Printf("len=%d\tcap=%d\tval=%d\n", len(s2), cap(s2), s2)
}

输出结果:

代码语言:javascript
复制
// go 1.22
len=9   cap=9   val=[1 2 6 7 8 9 0 0 0]
len=6   cap=9   val=[1 2 6 7 8 9]
// go 1.21
len=9 cap=9 val=[1 2 6 7 8 9 7 8 9]
len=6 cap=9 val=[1 2 6 7 8 9]

阅读上面这段代码,我们可以发现 Go 1.22 标准库 slices 中的函数 Delete 将新切片长度 6 和旧切片长度 9 之间的元素改为切片元素的类型零值。

DeleteFunc

标准库 slices 的函数 func DeleteFunc[S ~[]E, E any](s S, del func(E) bool) S 在切片 s 中删除函数类型参数 del 返回值为 true 的任意元素,返回修改后的切片。

示例代码:

代码语言:javascript
复制
func main() {
 s1 := []int{1, 2, 3, 4, 5, 6, 7, 8, 9}
 s2 := slices.DeleteFunc(s1, func(i int) bool {
  return i%2 != 0
 })
 fmt.Printf("len=%d\tcap=%d\tval=%d\t\n", len(s1), cap(s1), s1)
 fmt.Printf("len=%d\tcap=%d\tval=%d\t\n", len(s2), cap(s2), s2)

输出结果:

代码语言:javascript
复制
// go 1.22
len=9   cap=9   val=[2 4 6 8 0 0 0 0 0] 
len=4   cap=9   val=[2 4 6 8]
// go 1.21
len=9 cap=9 val=[2 4 6 8 5 6 7 8 9] 
len=4 cap=9 val=[2 4 6 8]

阅读上面这段代码,我们可以发现 Go 1.22 标准库 slices 中的函数 DeleteFunc 将新切片长度 4 和旧切片长度 9 之间的元素改为切片元素的类型零值。

Compact

标准库 slices 的函数 func Compact[S ~[]E, E comparable](s S) S 将切片中连续重复的元素去重。Compact 修改切片的内容并返回修改后的切片,该切片的长度可能比原切片的长度小。

示例代码:

代码语言:javascript
复制
func main() {
 s1 := []int{1, 2, 2, 3, 4, 3, 5, 4}
 s2 := slices.Compact(s1)
 fmt.Printf("len=%d\tcap=%d\tval=%d\t\n", len(s1), cap(s1), s1)
 fmt.Printf("len=%d\tcap=%d\tval=%d\t\n", len(s2), cap(s2), s2)
}

输出结果:

代码语言:javascript
复制
// go 1.22
len=8   cap=8   val=[1 2 3 4 3 5 4 0]   
len=7   cap=8   val=[1 2 3 4 3 5 4]
// go 1.21
len=8 cap=8 val=[1 2 3 4 3 5 4 4] 
len=7 cap=8 val=[1 2 3 4 3 5 4]

阅读上面这段代码,我们可以发现 Go 1.22 标准库 slices 中的函数 Compact 将新切片长度 7 和旧切片长度 8 之间的元素改为切片元素的类型零值。

CompactFunc

标准库 slices 的函数 func CompactFunc[S ~[]E, E any](s S, eq func(E, E) bool) S 在切片 s 中将函数类型参数 eq 返回值为 true 的两个元素去重,返回修改后的切片。

示例代码:

代码语言:javascript
复制
func main() {
 s1 := []string{"go", "php", "php", "java", "python"}
 s2 := slices.CompactFunc(s1, strings.EqualFold)
 fmt.Printf("len=%d\tcap=%d\tval=%#v\n", len(s1), cap(s1), s1)
 fmt.Printf("len=%d\tcap=%d\tval=%#v\n", len(s2), cap(s2), s2)
}

输出结果:

代码语言:javascript
复制
// go 1.22
len=5   cap=5   val=[]string{"go", "php", "java", "python", ""}
len=4   cap=5   val=[]string{"go", "php", "java", "python"}
// go 1.21
len=5 cap=5 val=[]string{"go", "php", "java", "python", "python"}
len=4 cap=5 val=[]string{"go", "php", "java", "python"}

阅读上面这段代码,我们可以发现 Go 1.22 标准库 slices 中的函数 CompactFunc 将新切片长度 4 和旧切片长度 5 之间的元素改为切片元素的类型零值。

Replace

标准库 slices 函数 func Replace[S ~[]E, E any](s S, i, j int, v ...E) S 将切片 ss[i:j] 中的元素修改为 v,返回修改后的切片。

示例代码:

代码语言:javascript
复制
func main() {
 s1 := []string{"php", "java", "go", "python"}
 s2 := slices.Replace(s1, 1, 3, "rust")
 fmt.Printf("len=%d\tcap=%d\tval=%#v\n", len(s1), cap(s1), s1)
 fmt.Printf("len=%d\tcap=%d\tval=%#v\n", len(s2), cap(s2), s2)
}

输出结果:

代码语言:javascript
复制
// go 1.22
len=4   cap=4   val=[]string{"php", "rust", "python", ""}
len=3   cap=4   val=[]string{"php", "rust", "python"}
// go 1.21
len=4 cap=4 val=[]string{"php", "rust", "python", "python"}
len=3 cap=4 val=[]string{"php", "rust", "python"}

阅读上面这段代码,,我们可以发现 Go 1.22 标准库 slices 中的函数 Replacelen(v) < (j-i) 时,将新切片长度 2 和旧切片长度 3 之间的元素改为切片元素的类型零值。

04

函数 Insert 越界优化

Go 1.22 优化 Insert 函数,当参数 i 超出切片范围,则 Insert 函数将运行时触发 panic,此前,如果没有要插入的元素,该情况运行时不会触发 panic

我们通过示例代码,分别在 Go 1.22 和 Go 1.21 中运行,查看运行结果的变化。

Insert

标准库 slices 的函数 func Insert[S ~[]E, E any](s S, i int, v ...E) S 在切片 si 索引位置,插入元素 v,返回修改后的切片(s[i:] 处的元素被向上移动以腾出空间)。

示例代码:

代码语言:javascript
复制
func main() {
 s1 := []string{"Go", "PHP", "Java", "Rust"}
 s2 := slices.Insert(s1, 5)
 fmt.Printf("len=%d\tcap=%d\tval=%#v\n", len(s1), cap(s1), s1)
 fmt.Printf("len=%d\tcap=%d\tval=%#v\n", len(s2), cap(s2), s2)
}

输出结果:

代码语言:javascript
复制
// go 1.22
panic: runtime error: slice bounds out of range [5:4]
// ...
// go 1.21
len=4 cap=4 val=[]string{"Go", "PHP", "Java", "Rust"}
len=4 cap=4 val=[]string{"Go", "PHP", "Java", "Rust"}

阅读上面这段代码,我们可以发现 Go 1.22 标准库 slices 中的函数 Inserti 超出切片的范围,即使没有插入元素,运行时也会触发 panic

需要注意的是,如果在 i 超出切片的范围时,插入新元素,Go 1.22 和 Go 1.21 运行时都会引发 panic

05

总结

本文我们介绍 Go 1.22 关于标准库 slices 的新增函数 Concat 和一些旧函数的优化。

其中,新增函数 Concat 使连接多个切片为一个切片的实现代码更加优雅;其余优化函数对比 go 1.21 的变化,也需要我们特别关注。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2024-03-23,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Golang语言开发栈 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档