写点什么

Go 1.22 slices 库的更新:高效拼接、零化处理和越界插入优化

作者:陈明勇
  • 2024-02-19
    广东
  • 本文字数:4260 字

    阅读完需:约 14 分钟

Go 1.22 slices 库的更新:高效拼接、零化处理和越界插入优化

前言

Go 1.22 版本于 202426 日发布,引入了几个重要的特性和改进。在标准库层面上,该版本对 slices 库进行了更新,更新内容包括以下三个方面:


  • 新增 Concat 函数:该函数能够高效地拼接多个切片。

  • 零化处理:DeleteDeleteFuncCompactCompactFuncReplace 函数在原切片中将 被移除的元素 置为零值(被移除的元素 是指从原切片中移除的指定元素,在新切片中不存在)。

  • 越界插入优化:在使用 Insert 函数时,若参数 i 超出切片的范围,则总会触发 panic。而在 Go 1.22 版本之前,即使 i 越界了,在没有指定插入元素的情况下,该行为不会触发 panic


本文将详细介绍 Go 语言 slices 库在 Go 1.22 版本中的更新内容。


准备好了吗?准备一杯你最喜欢的咖啡或茶,随着本文一探究竟吧。



新增函数 Concat:高效拼接切片

Concat 函数接受一个不定参数 slices,参数类型为切片,该函数用于将多个切片拼接到一个新的切片里并返回新切片。


代码示例:


package main
import ( "fmt" "slices")
func main() { s1 := []string{"Go slices", "Go maps"} s2 := []string{"Go strings", "Go strconv"} s3 := []string{"程序员", "陈明勇"} s4 := slices.Concat(s1, s2, s3) fmt.Printf("cap: %d, len: %d\n", cap(s4), len(s4)) fmt.Println(s4)}
复制代码


代码运行结果如下所示:


cap: 6, len: 6[Go slices Go maps Go strings Go strconv 程序员 陈明勇]
复制代码


根据运行结果可知,Concat 函数将所给定的切片集的元素都拼接到一个新切片里,并且新切片的容量和长度是所给定切片集长度的总和。


我们来看看 Concat 函数的源码实现:


// 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}
复制代码


Concat 函数的源码实现非常简洁,它在拼接切片之前先计算了新切片所需的长度,然后利用 Grow 函数初始化新切片。这样做的好处是避免了后续 append 操作中因为切片扩容而导致的内存重新分配和复制问题,使得函数更加高效。


这里留一个悬念:你们知道在什么情况下, if size < 0 这个看似不会成立的分支会成立吗?^_^ 欢迎在评论区发表你的见解。

零化处理

Go 1.22 版本中,对 DeleteDeleteFuncCompactCompactFuncReplace 函数进行了更新。这些函数的共同点是接受一个给定的切片参数,记为 s1,并返回一个新切片,记为 s2。被移除的元素会在 s1 中被置为零值(被移除的元素 是指从 s1 中移除的指定元素,在s2 中不存在)。

Delete 函数

通过不同 Go 版本的代码示例来感受 Delete 函数 零化处理 的更新。


  • Go 1.21 版本的代码示例


    package main
import ( "fmt" "slices" )
func main() { s1 := []int{1, 2, 3, 4, 5} s2 := slices.Delete(s1, 3, 5) fmt.Println(s1) fmt.Println(s2) }
复制代码

代码运行结果如下所示:

    [1 2 3 4 5]    [1 2 3]
复制代码


  • Go 1.22 版本的代码示例


    package main
import ( "fmt" "slices" )
func main() { s1 := []int{1, 2, 3, 4, 5} s2 := slices.Delete(s1, 3, 5) fmt.Println(s1) fmt.Println(s2) }
复制代码


代码运行结果如下所示:


    [1 2 3 0 0]    [1 2 3]
复制代码


通过对比不同版本的代码运行结果可知,被移除的元素 在原切片里被置为了 零值

DeleteFunc 函数

通过不同 Go 版本的代码示例来感受 DeleteFunc 函数 零化处理 的更新。


  • Go 1.21 版本的代码示例


    package main
import ( "fmt" "slices" )
func main() { s1 := []int{1, 2, 3, 4, 5} s2 := slices.DeleteFunc(s1, func(e int) bool { return e%2 == 0 }) fmt.Println(s1) fmt.Println(s2) }
复制代码


代码运行结果如下所示:


    [1 3 5 4 5]    [1 3 5]
复制代码


  • Go 1.22 版本的代码示例


    package main
import ( "fmt" "slices" )
func main() { s1 := []int{1, 2, 3, 4, 5} s2 := slices.DeleteFunc(s1, func(e int) bool { return e%2 == 0 }) fmt.Println(s1) fmt.Println(s2) }
复制代码


代码运行结果如下所示:


    [1 3 5 0 0]    [1 3 5]
复制代码


通过对比不同版本的代码运行结果可知,被移除的元素 在原切片里被置为了 零值

Compact 函数

通过不同 Go 版本的代码示例来感受 Compact 函数 零化处理 的更新。


  • Go 1.21 版本的代码示例


    package main
import ( "fmt" "slices" )
func main() { s1 := []int{1, 2, 2, 3, 3, 4, 5} s2 := slices.Compact(s1) fmt.Println(s1) fmt.Println(s2) }
复制代码


代码运行结果如下所示:


    [1 2 3 4 5 4 5]    [1 2 3 4 5]
复制代码


  • Go 1.22 版本的代码示例


    package main
import ( "fmt" "slices" )
func main() { s1 := []int{1, 2, 2, 3, 3, 4, 5} s2 := slices.Compact(s1) fmt.Println(s1) fmt.Println(s2) }
复制代码


代码运行结果如下所示:


    [1 2 3 4 5 0 0]    [1 2 3 4 5]
复制代码


通过对比不同版本的代码运行结果可知,被移除的元素 在原切片里被置为了 零值

CompactFunc 函数

通过不同 Go 版本的代码示例来感受 CompactFunc 函数 零化处理 的更新。


  • Go 1.21 版本的代码示例


    package main
import ( "fmt" "slices" "strings" )
func main() { s1 := []string{"Gopher", "MingYong Chen", "mingyong chen"} s2 := slices.CompactFunc(s1, func(a, b string) bool { return strings.ToLower(a) == strings.ToLower(b) }) fmt.Printf("%#v\n", s1) fmt.Printf("%#v\n", s2) }
复制代码


代码运行结果如下所示:


    []string{"Gopher", "MingYong Chen", "mingyong chen"}    []string{"Gopher", "MingYong Chen"}
复制代码


  • Go 1.22 版本的代码示例


    package main
import ( "fmt" "slices" "strings" )
func main() { s1 := []string{"Gopher", "MingYong Chen", "mingyong chen"} s2 := slices.CompactFunc(s1, func(a, b string) bool { return strings.ToLower(a) == strings.ToLower(b) }) fmt.Printf("%#v\n", s1) fmt.Printf("%#v\n", s2) }
复制代码


代码运行结果如下所示:


    []string{"Gopher", "MingYong Chen", ""}    []string{"Gopher", "MingYong Chen"}
复制代码


通过对比不同版本的代码运行结果可知,被移除的元素 在原切片里被置为了 零值

Replace 函数

通过不同 Go 版本的代码示例来感受 Replace 函数 零化处理 的更新。


  • Go 1.21 版本的代码示例


    package main
import ( "fmt" "slices" )
func main() { s1 := []int{1, 6, 7, 4, 5} s2 := slices.Replace(s1, 1, 3, 2) fmt.Println(s1) fmt.Println(s2) }
复制代码


代码运行结果如下所示:


    [1 2 4 5 5]    [1 2 4 5]
复制代码


  • Go 1.22 版本的代码示例


    package main
import ( "fmt" "slices" )
func main() { s1 := []int{1, 6, 7, 4, 5} s2 := slices.Replace(s1, 1, 3, 2) fmt.Println(s1) fmt.Println(s2) }
复制代码


代码运行结果如下所示:


    [1 2 4 5 0]    [1 2 4 5]
复制代码


在示例代码中,主要功能是将元素 2 替换原切片中 [1, 3] 区间的元素。因为一个元素替换了两个元素,所以第二个元素会被移除。通过对比不同版本的代码运行结果可知,被移除的元素 在原切片里被置为了 零值

越界插入优化

Go 1.22 版本对 slices 库的 Insert 函数进行了优化。在使用 Insert 函数时,若参数 i 超出切片的范围,总会触发 panic。而在 Go 1.22 版本之前,即使 i 越界了,在没有指定插入元素的情况下,该行为不会触发 panic


通过不同 Go 版本的代码示例来感受该优化。


  • Go 1.21 版本的代码示例


    package main
import ( "fmt" "slices" )
func main() { s1 := []string{"程序员", "陈明勇"} s2 := slices.Insert(s1, 3) fmt.Println(s2) }
复制代码


代码运行结果如下所示:


    [程序员 陈明勇]
复制代码


  • Go 1.22 版本的代码示例


    package main
import ( "fmt" "slices" )
func main() { s1 := []string{"程序员", "陈明勇"} s2 := slices.Insert(s1, 3) fmt.Println(s2) }
复制代码


代码运行结果如下所示:


    panic: runtime error: slice bounds out of range [3:2]
goroutine 1 [running]: slices.Insert[...]({0xc000068020?, 0xc000046740?, 0x0?}, 0x60?, {0x0?, 0xc000076058?, 0x524258?}) /usr/local/go-faketime/src/slices/slices.go:133 +0x486 main.main() /tmp/sandbox4036609674/prog.go:12 +0x68
复制代码


在示例代码中,调用 slices.Insert 函数时,仅传递了切片 s 和插入位置索引 i 参数,而缺少了待插入元素值 v 参数。


通过对比不同版本的代码运行结果可知,在 Go 1.21 版本下,该代码正常运行结束而不会触发 panic,但在 Go 1.22 版本下,会触发 panic

小结

本文详细介绍了 Go 1.22 版本中 slices 库的更新内容,总结起来有三个方面:


  • 新增了 Concat 函数。

  • 对部分函数新增了零化处理的逻辑,包括 DeleteDeleteFuncCompactCompactFuncReplace 函数。

  • Insert 函数进行了越界插入优化。

推荐阅读


发布于: 刚刚阅读数: 3
用户头像

陈明勇

关注

一个热爱技术,喜欢专研技术的程序员。 2021-10-20 加入

公众号:Go技术干货

评论

发布
暂无评论
Go 1.22 slices 库的更新:高效拼接、零化处理和越界插入优化_Go_陈明勇_InfoQ写作社区