Go語言利用泛型封裝常見的Map操作
什么是泛型
泛型是一種編程范式,允許開發(fā)者在編寫代碼時(shí)定義通用的類型參數(shù),而不是具體的類型。通過泛型,可以編寫出能夠處理多種數(shù)據(jù)類型的代碼,而無需為每種類型重復(fù)編寫相同的邏輯。例如,一個(gè)泛型函數(shù)可以同時(shí)處理整數(shù)、浮點(diǎn)數(shù)、字符串等多種類型的數(shù)據(jù)。
泛型解決了什么問題
在 Go 語言引入泛型之前,開發(fā)者在處理不同數(shù)據(jù)類型時(shí),往往需要編寫重復(fù)的代碼。例如,實(shí)現(xiàn)一個(gè)排序算法,可能需要為整數(shù)、浮點(diǎn)數(shù)、字符串等分別編寫不同的版本。這種重復(fù)不僅增加了代碼量,也降低了代碼的可維護(hù)性。引入泛型后,可以通過定義一個(gè)通用的類型參數(shù),編寫一個(gè)通用的排序函數(shù),從而提高代碼的復(fù)用性和可維護(hù)性。
Go泛型
Go 語言在 1.18 版本中引入了泛型,這是 Go 語言發(fā)展的一個(gè)重要里程碑,它極大地增強(qiáng)了語言的表達(dá)能力和靈活性。
基于泛型的常見Map操作
在上一篇文章里,我們使用Go泛型打造了一個(gè)優(yōu)雅的切片工具庫,本篇博客將利用Go泛型,封裝常見的Map操作,并配套相應(yīng)的單元測(cè)試。本篇博客所編寫的代碼,皆可直接集成到生產(chǎn)環(huán)境的公共代碼庫中。各位小伙伴可以根據(jù)自身項(xiàng)目的實(shí)際情況,將對(duì)你們項(xiàng)目有幫助的代碼遷移到自己的項(xiàng)目當(dāng)中。
1.獲取map的所有keys
func GetMapKeys[K comparable, V any](m map[K]V) []K {
keys := make([]K, 0, len(m))
for k := range m {
keys = append(keys, k)
}
return keys
}
2.根據(jù)過濾條件獲取map的keys
func GetMapKeysMatchCondition[K comparable, V any](m map[K]V, condition func(K, V) bool) []K {
keys := make([]K, 0, len(m))
for k, v := range m {
if condition(k, v) {
keys = append(keys, k)
}
}
return keys
}
3.獲取map的所有values
func GetMapValues[K comparable, V any](m map[K]V) []V {
values := make([]V, 0, len(m))
for _, v := range m {
values = append(values, v)
}
return values
}
4.根據(jù)過濾條件獲取map的values
func GetMapValuesMatchCondition[K comparable, V any](m map[K]V, condition func(K, V) bool) []V {
values := make([]V, 0, len(m))
for k, v := range m {
if condition(k, v) {
values = append(values, v)
}
}
return values
}
5.合并多個(gè)map
func MergeMaps[K comparable, V any](maps ...map[K]V) map[K]V {
mergeMap := make(map[K]V)
for _, m := range maps {
for k, v := range m {
mergeMap[k] = v
}
}
return mergeMap
}6.map轉(zhuǎn)切片
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
}
7.切片轉(zhuǎn)map
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
}
8.復(fù)制map
func CopyMap[K comparable, V any](oldMap map[K]V) map[K]V {
newMap := make(map[K]V, len(oldMap))
for k, v := range oldMap {
newMap[k] = v
}
return newMap
}
9.sync.Map轉(zhuǎn)map
func SyncMapToMap[K comparable, V any](syncMap sync.Map) map[K]V {
m := make(map[K]V)
syncMap.Range(func(key, value any) bool {
// 嘗試將key和value轉(zhuǎn)換為指定的類型
k, ok1 := key.(K)
v, ok2 := value.(V)
if ok1 && ok2 {
m[k] = v
}
return true
})
return m
}
10.map轉(zhuǎn)sync.Map
func MapToSyncMap[K comparable, V any](m map[K]V) *sync.Map {
syncMap := &sync.Map{}
if m == nil {
return syncMap
}
for k, v := range m {
syncMap.Store(k, v)
}
return syncMap
}
代碼合集
我將上述所有代碼集成到一個(gè)maps.go文件當(dāng)中,并配套了單元測(cè)試文件maps_test.go。如果各位小伙伴們的項(xiàng)目有需要,只需將以下代碼完整拷貝到你們項(xiàng)目的基礎(chǔ)代碼工具庫即可。
maps.go
package maps
import (
"sync"
)
// 獲取map的所有keys
func GetMapKeys[K comparable, V any](m map[K]V) []K {
keys := make([]K, 0, len(m))
for k := range m {
keys = append(keys, k)
}
return keys
}
// 根據(jù)過濾條件獲取map的keys
func GetMapKeysMatchCondition[K comparable, V any](m map[K]V, condition func(K, V) bool) []K {
keys := make([]K, 0, len(m))
for k, v := range m {
if condition(k, v) {
keys = append(keys, k)
}
}
return keys
}
func GetMapValues[K comparable, V any](m map[K]V) []V {
values := make([]V, 0, len(m))
for _, v := range m {
values = append(values, v)
}
return values
}
// 根據(jù)過濾條件獲取map的values
func GetMapValuesMatchCondition[K comparable, V any](m map[K]V, condition func(K, V) bool) []V {
values := make([]V, 0, len(m))
for k, v := range m {
if condition(k, v) {
values = append(values, v)
}
}
return values
}
// 合并多個(gè)map
func MergeMaps[K comparable, V any](maps ...map[K]V) map[K]V {
mergeMap := make(map[K]V)
for _, m := range maps {
for k, v := range m {
mergeMap[k] = v
}
}
return mergeMap
}
// map轉(zhuǎn)切片
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
}
// 切片轉(zhuǎn)map
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
}
// 復(fù)制map
func CopyMap[K comparable, V any](oldMap map[K]V) map[K]V {
newMap := make(map[K]V, len(oldMap))
for k, v := range oldMap {
newMap[k] = v
}
return newMap
}
// sync.Map轉(zhuǎn)map
func SyncMapToMap[K comparable, V any](syncMap sync.Map) map[K]V {
m := make(map[K]V)
syncMap.Range(func(key, value any) bool {
// 嘗試將key和value轉(zhuǎn)換為指定的類型
k, ok1 := key.(K)
v, ok2 := value.(V)
if ok1 && ok2 {
m[k] = v
}
return true
})
return m
}
func MapToSyncMap[K comparable, V any](m map[K]V) *sync.Map {
syncMap := &sync.Map{}
if m == nil {
return syncMap
}
for k, v := range m {
syncMap.Store(k, v)
}
return syncMap
}
maps_test.go
package maps
import (
"sync"
"testing"
)
// 測(cè)試 GetMapKeys 函數(shù)
func TestGetMapKeys(t *testing.T) {
m := map[string]int{
"apple": 1,
"banana": 2,
"cherry": 3,
}
keys := GetMapKeys(m)
if len(keys) != len(m) {
t.Errorf("Expected %d keys, got %d", len(m), len(keys))
}
for _, k := range keys {
if _, exists := m[k]; !exists {
t.Errorf("Key %s not found in original map", k)
}
}
}
// 測(cè)試 GetMapKeysMatchCondition 函數(shù)
func TestGetMapKeysMatchCondition(t *testing.T) {
m := map[string]int{
"apple": 1,
"banana": 2,
"cherry": 3,
}
condition := func(k string, v int) bool {
return v > 1
}
keys := GetMapKeysMatchCondition(m, condition)
for _, k := range keys {
if m[k] <= 1 {
t.Errorf("Key %s should not be included as its value is not greater than 1", k)
}
}
}
// 測(cè)試 GetMapValues 函數(shù)
func TestGetMapValues(t *testing.T) {
m := map[string]int{
"apple": 1,
"banana": 2,
"cherry": 3,
}
values := GetMapValues(m)
if len(values) != len(m) {
t.Errorf("Expected %d values, got %d", len(m), len(values))
}
valueSet := make(map[int]bool)
for _, v := range values {
valueSet[v] = true
}
for _, v := range m {
if !valueSet[v] {
t.Errorf("Value %d not found in result values", v)
}
}
}
// 測(cè)試 GetMapValuesMatchCondition 函數(shù)
func TestGetMapValuesMatchCondition(t *testing.T) {
m := map[string]int{
"apple": 1,
"banana": 2,
"cherry": 3,
}
condition := func(k string, v int) bool {
return v > 1
}
values := GetMapValuesMatchCondition(m, condition)
for _, v := range values {
if v <= 1 {
t.Errorf("Value %d should not be included as it is not greater than 1", v)
}
}
}
// 測(cè)試 MergeMaps 函數(shù)
func TestMergeMaps(t *testing.T) {
m1 := map[string]int{
"apple": 1,
"banana": 2,
}
m2 := map[string]int{
"banana": 3,
"cherry": 4,
}
merged := MergeMaps(m1, m2)
for k := range m1 {
if _, exists := merged[k]; !exists {
t.Errorf("key %s not exist in m1", k)
}
}
for k := range m2 {
if _, exists := merged[k]; !exists {
t.Errorf("key %s not exist in m2", k)
}
}
}
// 測(cè)試 MapToSlice 函數(shù)
func TestMapToSlice(t *testing.T) {
m := map[string]int{
"apple": 1,
"banana": 2,
"cherry": 3,
}
extractor := func(v int) int {
return v * 2
}
slice := MapToSlice(m, extractor)
if len(slice) != len(m) {
t.Errorf("Expected %d elements in slice, got %d", len(m), len(slice))
}
for _, v := range m {
found := false
for _, s := range slice {
if s == v*2 {
found = true
break
}
}
if !found {
t.Errorf("Transformed value %d not found in slice", v*2)
}
}
}
// 測(cè)試 SliceToMap 函數(shù)
func TestSliceToMap(t *testing.T) {
s := []int{1, 2, 3}
keyFunc := func(v int) int {
return v * 10
}
m := SliceToMap(s, keyFunc)
if len(m) != len(s) {
t.Errorf("Expected %d keys in map, got %d", len(s), len(m))
}
for _, v := range s {
key := keyFunc(v)
if val, exists := m[key]; !exists || val != v {
t.Errorf("Value for key %d in map does not match original slice", key)
}
}
}
// 測(cè)試 CopyMap 函數(shù)
func TestCopyMap(t *testing.T) {
oldMap := map[string]int{
"apple": 1,
"banana": 2,
"cherry": 3,
}
newMap := CopyMap(oldMap)
if len(newMap) != len(oldMap) {
t.Errorf("Expected %d keys in new map, got %d", len(oldMap), len(newMap))
}
for k, v := range oldMap {
if val, exists := newMap[k]; !exists || val != v {
t.Errorf("Value for key %s in new map does not match original map", k)
}
}
}
// 測(cè)試 SyncMapToMap 函數(shù)
func TestSyncMapToMap(t *testing.T) {
var syncMap sync.Map
syncMap.Store("apple", 1)
syncMap.Store("banana", 2)
syncMap.Store("cherry", 3)
m := SyncMapToMap[string, int](syncMap)
syncMap.Range(func(key, value any) bool {
k := key.(string)
v := value.(int)
if val, exists := m[k]; !exists || val != v {
t.Errorf("Value for key %s in map does not match sync.Map", k)
}
return true
})
}
// 測(cè)試 MapToSyncMap 函數(shù)
func TestMapToSyncMap(t *testing.T) {
m := map[string]int{
"apple": 1,
"banana": 2,
"cherry": 3,
}
syncMap := MapToSyncMap(m)
for k, v := range m {
value, exists := syncMap.Load(k)
if !exists || value.(int) != v {
t.Errorf("Value for key %s in sync.Map does not match original map", k)
}
}
}
總結(jié)
本文使用Go泛型,對(duì)常見的Map操作進(jìn)行了封裝,整理出了一個(gè)Map工具庫maps.go。
以上就是Go語言利用泛型封裝常見的Map操作的詳細(xì)內(nèi)容,更多關(guān)于Go泛型封裝Map操作的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
GoLang strings.Builder底層實(shí)現(xiàn)方法詳解
自從學(xué)習(xí)go一個(gè)月以來,我多少使用了一下strings.Builder,略有心得。你也許知道它,特別是你了解bytes.Buffer的話。所以我在此分享一下我的心得,并希望能對(duì)你有所幫助2022-10-10
golang框架gin的日志處理和zap lumberjack日志使用方式
這篇文章主要介紹了golang框架gin的日志處理和zap lumberjack日志使用方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-01-01
golang 歸并排序,快速排序,堆排序的實(shí)現(xiàn)
本文主要介紹了golang 歸并排序,快速排序,堆排序的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-01-01
Gin+Gorm實(shí)現(xiàn)增刪改查的示例代碼
本文介紹了如何使用Gin和Gorm框架實(shí)現(xiàn)一個(gè)簡(jiǎn)單的增刪改查(CRUD)示例,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2024-12-12
深入了解Go語言中web框架的中間件運(yùn)行機(jī)制
大家在使用iris框架搭建web系統(tǒng)時(shí),一定會(huì)用到中間件。那么你了解中間件的運(yùn)行機(jī)制嗎?你知道為什么在iris和gin框架的請(qǐng)求處理函數(shù)中要加c.Next()函數(shù)嗎?本文就和大家一起探究該問題的答案2023-02-02

