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

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

01


介绍


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


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


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


02


新增函数 Concat


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


示例代码:


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)
}


输出结果:


[1 2 3 4 5 6 7 8 9]


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


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


示例代码:


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)
}


输出结果:


[1 2 3 4 5 6 7 8 9]


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


源码实现:


// 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] 中的元素,返回修改后的切片。


示例代码:


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)
}


输出结果:


// 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 的任意元素,返回修改后的切片。


示例代码:


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)


输出结果:


// 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 修改切片的内容并返回修改后的切片,该切片的长度可能比原切片的长度小。


示例代码:


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)
}


输出结果:


// 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 的两个元素去重,返回修改后的切片。


示例代码:


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)
}


输出结果:


// 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,返回修改后的切片。


示例代码:


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)
}


输出结果:


// 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:] 处的元素被向上移动以腾出空间)。


例代码:


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)
}


输出结果:


// 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 的变化,也需要我们特别关注。


推荐阅读


  1. Prometheus 的查询语言 PromQL 详解
  2. Go 语言使用标准库 sync 包的 mutex 互斥锁解决数据竞态
  3. Golang 语言标准库 sync 包的 RWMutex 读写互斥锁怎么使用?
  4. Golang语言标准库 sync 包的 WaitGroup 怎么使用?
  5. Golang语言标准库 sync 包的 Cond 怎么使用?
目录
打赏
0
6
6
0
75
分享
相关文章
通义灵码 Rules 库合集来了,覆盖Java、TypeScript、Python、Go、JavaScript 等
通义灵码新上的外挂 Project Rules 获得了开发者的一致好评:最小成本适配我的开发风格、相当把团队经验沉淀下来,是个很好功能……
477 101
|
17天前
|
【LeetCode 热题100】深入理解二叉树结构变化与路径特性(力扣104 / 226 / 114 / 543)(Go语言版)
本博客深入探讨二叉树的深度计算、结构变换与路径分析,涵盖四道经典题目:104(最大深度)、226(翻转二叉树)、114(展开为链表)和543(二叉树直径)。通过递归与遍历策略(前序、后序等),解析每题的核心思路与实现方法。结合代码示例(Go语言),帮助读者掌握二叉树相关算法的精髓。下一讲将聚焦二叉树构造问题,欢迎持续关注!
47 10
|
2月前
|
go-carbon v2.6.0 重大版本更新,轻量级、语义化、对开发者友好的 golang 时间处理库
carbon 是一个轻量级、语义化、对开发者友好的 Golang 时间处理库,提供了对时间穿越、时间差值、时间极值、时间判断、星座、星座、农历、儒略日 / 简化儒略日、波斯历 / 伊朗历的支持
68 3
Go 1.24.0 重磅发布:新特性、新工具,开发者必看!
`Go 1.24.0` 已正式发布,带来诸多改进和新特性。语言层面上,泛型类型别名现已被完全支持;性能方面,通过优化 `map` 实现和内存分配,减少了 2-3% 的 CPU 开销;工具链新增模块工具依赖跟踪及测试分析器;标准库增加了弱引用包、FIPS 140-3 合规机制等;WebAssembly 支持也得到了增强。快来下载体验吧!
192 7
用 Go 基于 epoll 实现一个最小化的IO库
Go 语言社区中存在多个异步网络框架,如 evio、nbio、gnet 和 netpoll 等。这些框架旨在解决标准库 netpoll 的低效问题,如一个连接占用一个 goroutine 导致的资源浪费。easyio 是一个最小化的 IO 框架,核心代码不超过 500 行,仅实现 Linux 下的 epoll 和 TCP 协议。它通过 Worker Pool、Buffer 等优化提高了性能,并提供了简单的事件处理机制。
|
6月前
|
go-carbon v2.5.0 发布,轻量级、语义化、对开发者友好的 golang 时间处理库
carbon 是一个轻量级、语义化、对开发者友好的 Golang 时间处理库,提供了对时间穿越、时间差值、时间极值、时间判断、星座、星座、农历、儒略日 / 简化儒略日、波斯历 / 伊朗历的支持。
140 4
go库介绍:Golang中的Viper库
Viper 是 Golang 中的一个强大配置管理库,支持环境变量、命令行参数、远程配置等多种配置来源。本文详细介绍了 Viper 的核心特点、应用场景及使用方法,并通过示例展示了其强大功能。无论是简单的 CLI 工具还是复杂的分布式系统,Viper 都能提供优雅的配置管理方案。
146 6
go语言使用内置函数和标准库
【10月更文挑战第18天】
53 3
Go语言的并发特性
【10月更文挑战第26天】Go语言的并发特性
54 1

热门文章

最新文章

AI助理

你好,我是AI助理

可以解答问题、推荐解决方案等