Go语言泛型打造优雅的切片工具库

 更新时间:2025年02月06日 08:34:02   作者:gopher_looklook  
泛型是一种编程范式,允许开发者在编写代码时定义通用的类型参数,而不是具体的类型,本文将使用泛型实现打造优雅的切片工具库,希望对大家有所帮助

什么是泛型

泛型是一种编程范式,允许开发者在编写代码时定义通用的类型参数,而不是具体的类型。通过泛型,可以编写出能够处理多种数据类型的代码,而无需为每种类型重复编写相同的逻辑。例如,一个泛型函数可以同时处理整数、浮点数、字符串等多种类型的数据。

泛型解决了什么问题

在 Go 语言引入泛型之前,开发者在处理不同数据类型时,往往需要编写重复的代码。例如,实现一个排序算法,可能需要为整数、浮点数、字符串等分别编写不同的版本。这种重复不仅增加了代码量,也降低了代码的可维护性。引入泛型后,可以通过定义一个通用的类型参数,编写一个通用的排序函数,从而提高代码的复用性和可维护性。

基于泛型的常见切片操作

博主结合自身在实际开发当中的经验,将利用Go泛型,封装一些常见的切片操作。本篇博客所编写的代码,皆可直接集成到生产环境的公共代码库中。各位小伙伴可以根据自身项目的实际情况,将对你们项目有帮助的代码迁移到自己的项目当中。

1.反转切片(改变原切片)

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.反转切片(不改变原切片)

func ReverseSlice[T any](s []T) []T {
    res := make([]T, len(s))
    copy(res, s)
    ReverseOriginalSlice(res) // 调用之前的ReverseOriginalSlice函数
    return res
}

3.切片分批

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.合并切片

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.切片去重

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.切片转哈希表

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.哈希表转切片

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.获取切片元素的某个字段

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.切片全部元素满足条件判断

func SliceMatchCondition[T any](s []T, condition func(T) bool) bool {
    for _, v := range s {
       if !condition(v) {
          return false
       }
    }
    return true
}

10.取切片交集

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
}

11.取切片并集

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

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

到此这篇关于Go语言泛型打造优雅的切片工具库的文章就介绍到这了,更多相关Go泛型内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 详解Go函数和方法之间有什么区别

    详解Go函数和方法之间有什么区别

    这篇文章就简单和大家聊一聊在Go中函数与方法之间的区别,文章通过代码示例介绍的非常详细,对我们的学习或工作有一定的帮助,感兴趣的小伙伴跟着小编一起来看看吧
    2023-07-07
  • GO语言结构体面向对象操作示例

    GO语言结构体面向对象操作示例

    这篇文章主要介绍了GO语言编程中结构体面向对象的操作示例,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步早日升职加薪
    2022-04-04
  • Golang标准库binary详解

    Golang标准库binary详解

    这篇文章主要介绍了Golang标准库binary的相关资料,本文通过示例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2023-05-05
  • Go实现自动解压缩包以及读取docx/doc文件内容详解

    Go实现自动解压缩包以及读取docx/doc文件内容详解

    在开发过程中,我们常常需要处理压缩包和文档文件。本文将介绍如何使用Go语言自动解压缩包和读取docx/doc文件,需要的可以参考一下
    2023-03-03
  • 如何使用Go检测用户本地是否安装chrome

    如何使用Go检测用户本地是否安装chrome

    这篇文章主要为大家详细介绍了如何使用Go检测用户本地是否安装chrome,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下
    2024-10-10
  • 关于Golang标准库flag的全面讲解

    关于Golang标准库flag的全面讲解

    这篇文章主要介绍了关于Golang标准库flag的全面讲解,这个库的代码量只有1000行左右,却提供了非常完善的命令行参数解析功能,更多相关内容需要的朋友可以参考一下
    2022-09-09
  • 深度解密 Go 语言中的 sync.Pool

    深度解密 Go 语言中的 sync.Pool

    sync.Pool 是 sync 包下的一个组件,可以作为保存临时取还对象的一个“池子”。这篇文章主要介绍了深度解密 Go 语言中的sync.Pool,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-04-04
  • Golang高性能持久化解决方案BoltDB数据库介绍

    Golang高性能持久化解决方案BoltDB数据库介绍

    这篇文章主要为大家介绍了Golang高性能持久化解决方案BoltDB数据库介绍,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步
    2021-11-11
  • Go中时间与时区问题的深入讲解

    Go中时间与时区问题的深入讲解

    go语言中如果不设置指定的时区,通过time.Now()获取到的就是本地时区,下面这篇文章主要给大家介绍了关于Go中时间与时区问题的相关资料,需要的朋友可以参考下
    2021-12-12
  • golang 实现Location跳转方式

    golang 实现Location跳转方式

    这篇文章主要介绍了golang 实现Location跳转方式,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2021-05-05

最新评论