🐉大家好,我是gopher_looklook,现任某独角兽企业Go语言工程师,喜欢钻研Go源码,发掘各项技术在大型Go微服务项目中的最佳实践,期待与各位小伙伴多多交流,共同进步!
Go 语言在 1.18 版本中引入了泛型,这是 Go 语言发展的一个重要里程碑,它极大地增强了语言的表达能力和灵活性。
什么是泛型
泛型是一种编程范式,允许开发者在编写代码时定义通用的类型参数,而不是具体的类型。通过泛型,可以编写出能够处理多种数据类型的代码,而无需为每种类型重复编写相同的逻辑。例如,一个泛型函数可以同时处理整数、浮点数、字符串等多种类型的数据。
泛型解决了什么问题
在 Go 语言引入泛型之前,开发者在处理不同数据类型时,往往需要编写重复的代码。例如,实现一个排序算法,可能需要为整数、浮点数、字符串等分别编写不同的版本。这种重复不仅增加了代码量,也降低了代码的可维护性。引入泛型后,可以通过定义一个通用的类型参数,编写一个通用的排序函数,从而提高代码的复用性和可维护性。
基于泛型的常见切片操作
博主结合自身在实际开发当中的经验,将利用Go泛型,封装一些常见的切片操作。本篇博客所编写的代码,皆可直接集成到生产环境的公共代码库中。各位小伙伴可以根据自身项目的实际情况,将对你们项目有帮助的代码迁移到自己的项目当中。
- 反转切片(改变原切片)
go
func ReverseOriginalSlice[T any](s []T) {
for i, j := 0, len(s)-1; i < j; i, j = i+1, j-1 {
s[i], s[j] = s[j], s[i]
}
}
2.反转切片(不改变原切片)
go
func ReverseSlice[T any](s []T) []T {
res := make([]T, len(s))
copy(res, s)
ReverseOriginalSlice(res) // 调用之前的ReverseOriginalSlice函数
return res
}
3.切片分批
go
func BatchSlice[T any](s []T, size int) [][]T {
var batchSlice [][]T
// 遍历切片,每次取 size 个元素
for i := 0; i < len(s); i += size {
end := i + size
// 处理最后一批元素数量不足 size 的情况
if end > len(s) {
end = len(s)
}
// 将当前批次的元素添加到结果中
batchSlice = append(batchSlice, s[i:end])
}
return batchSlice
}
4.合并切片
go
func MergeSlices[T any](slices ...[]T) []T {
totalLength := 0
for _, slice := range slices {
totalLength += len(slice)
}
res := make([]T, 0, totalLength)
for _, slice := range slices {
ls := make([]T, len(slice))
copy(ls, slice)
res = append(res, ls...)
}
return res
}
5.切片去重
go
func UniqueSlice[T comparable](s []T) []T {
seen := make(map[T]bool)
res := make([]T, 0, len(s))
for _, v := range s {
if !seen[v] {
// 如果元素未出现过,添加到结果切片中
res = append(res, v)
seen[v] = true
}
}
return res
}
6.切片转哈希表
go
func SliceToMap[T any, K comparable](s []T, keyFunc func(T) K) map[K]T {
res := make(map[K]T)
for _, v := range s {
key := keyFunc(v)
res[key] = v
}
return res
}
7.哈希表转切片
go
func MapToSlice[K comparable, V any, T any](m map[K]V, extractor func(V) T) []T {
res := make([]T, 0, len(m))
for _, v := range m {
res = append(res, extractor(v))
}
return res
}
8.获取切片元素的某个字段
go
func GetListField[T any, V any](s []T, fieldFunc func(T) V) []V {
res := make([]V, 0, len(s))
for _, item := range s {
res = append(res, fieldFunc(item))
}
return res
}
9.切片全部元素满足条件判断
go
func SliceMatchCondition[T any](s []T, condition func(T) bool) bool {
for _, v := range s {
if !condition(v) {
return false
}
}
return true
}
- 取切片交集
go
func Intersection[T comparable](slices ...[]T) []T {
if len(slices) == 0 {
return nil
}
// 使用 map 来存储第一个切片中的元素
intersectionMap := make(map[T]int)
for _, v := range slices[0] {
intersectionMap[v]++
}
// 遍历后续切片,更新交集
for _, slice := range slices[1:] {
m := make(map[T]int)
for _, v := range slice {
if _, exists := intersectionMap[v]; exists {
m[v]++
}
}
intersectionMap = m
}
// 将交集的元素收集到结果切片中
var res []T
for k := range intersectionMap {
res = append(res, k)
}
return res
}
- 取切片并集
go
func Union[T comparable](slices ...[]T) []T {
elementMap := make(map[T]struct{})
for _, slice := range slices {
for _, v := range slice {
elementMap[v] = struct{}{}
}
}
var res []T
for k := range elementMap {
res = append(res, k)
}
return res
}
代码合集
我将上述所有代码集成到一个slices.go文件当中。如果各位小伙伴们的项目有需要,只需将以下代码完整拷贝到你们项目的基础代码工具库即可。
- slices.go
go
package slices
// 反转切片(改变原切片)
func ReverseOriginalSlice[T any](s []T) {
for i, j := 0, len(s)-1; i < j; i, j = i+1, j-1 {
s[i], s[j] = s[j], s[i]
}
}
// 反钻切片(不改变原切片)
func ReverseSlice[T any](s []T) []T {
res := make([]T, len(s))
copy(res, s)
ReverseOriginalSlice(res)
return res
}
// 切片分批
func BatchSlice[T any](s []T, size int) [][]T {
var batchSlice [][]T
for i := 0; i < len(s); i += size {
end := i + size
if end > len(s) {
end = len(s)
}
batchSlice = append(batchSlice, s[i:end])
}
return batchSlice
}
// 合并切片
func MergeSlices[T any](slices ...[]T) []T {
totalLength := 0
for _, slice := range slices {
totalLength += len(slice)
}
res := make([]T, 0, totalLength)
for _, slice := range slices {
ls := make([]T, len(slice))
copy(ls, slice)
res = append(res, ls...)
}
return res
}
// 切片去重
func UniqueSlice[T comparable](s []T) []T {
seen := make(map[T]bool)
res := make([]T, 0, len(s))
for _, v := range s {
if !seen[v] {
res = append(res, v)
seen[v] = true
}
}
return res
}
// 切片转哈希表
func SliceToMap[T any, K comparable](s []T, keyFunc func(T) K) map[K]T {
res := make(map[K]T)
for _, v := range s {
key := keyFunc(v)
res[key] = v
}
return res
}
// 哈希表转切片
func MapToSlice[K comparable, V any, T any](m map[K]V, extractor func(V) T) []T {
res := make([]T, 0, len(m))
for _, v := range m {
res = append(res, extractor(v))
}
return res
}
// 获取切片元素的某个字段
func GetListField[T any, V any](s []T, fieldFunc func(T) V) []V {
res := make([]V, 0, len(s))
for _, item := range s {
res = append(res, fieldFunc(item))
}
return res
}
// 切片全部元素满足条件判断
func SliceMatchCondition[T any](s []T, condition func(T) bool) bool {
for _, v := range s {
if !condition(v) {
return false
}
}
return true
}
// 取切片交集
func Intersection[T comparable](slices ...[]T) []T {
if len(slices) == 0 {
return nil
}
intersectionMap := make(map[T]int)
for _, v := range slices[0] {
intersectionMap[v]++
}
for _, slice := range slices[1:] {
m := make(map[T]int)
for _, v := range slice {
if _, exists := intersectionMap[v]; exists {
m[v]++
}
}
intersectionMap = m
}
var res []T
for k := range intersectionMap {
res = append(res, k)
}
return res
}
// 取切片并集
func Union[T comparable](slices ...[]T) []T {
elementMap := make(map[T]struct{})
for _, slice := range slices {
for _, v := range slice {
elementMap[v] = struct{}{}
}
}
var res []T
for k := range elementMap {
res = append(res, k)
}
return res
}
总结
本文使用Go泛型,对常见的切片操作进行了封装,整理出了一个切片工具库slices.go 。如果这篇文章对你有帮助,欢迎在生产环境中引入并二次封装本文的代码。也欢迎各位小伙伴提出一些补充或修改建议!