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)的單元測試。本篇博客所編寫的代碼,皆可直接集成到生產(chǎn)環(huán)境的公共代碼庫中。各位小伙伴可以根據(jù)自身項(xiàng)目的實(shí)際情況,將對你們項(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)中,并配套了單元測試文件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" ) // 測試 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) } } } // 測試 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) } } } // 測試 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) } } } // 測試 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) } } } // 測試 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) } } } // 測試 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) } } } // 測試 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) } } } // 測試 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) } } } // 測試 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 }) } // 測試 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泛型,對常見的Map操作進(jìn)行了封裝,整理出了一個(gè)Map工具庫maps.go。
以上就是Go語言利用泛型封裝常見的Map操作的詳細(xì)內(nèi)容,更多關(guān)于Go泛型封裝Map操作的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Golang Map實(shí)現(xiàn)賦值和擴(kuò)容的示例代碼
這篇文章主要介紹了Golang Map實(shí)現(xiàn)賦值和擴(kuò)容的示例代碼,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-04-04ubuntu安裝golang并設(shè)置goproxy的方法步驟
在Ubuntu系統(tǒng)上安裝Go語言(Golang)有多種方法,包括使用包管理器、從源代碼編譯安裝以及使用版本管理工具如gvm,安裝完成后,為了方便管理Go語言項(xiàng)目依賴,需要設(shè)置GOPATH環(huán)境變量并配置Go代理,本文介紹ubuntu安裝golang并設(shè)置goproxy的方法,感興趣的朋友一起看看吧2024-10-10Golang內(nèi)存對齊的規(guī)則及實(shí)現(xiàn)
本文介紹了Golang內(nèi)存對齊的規(guī)則及實(shí)現(xiàn),通過合理的內(nèi)存對齊,可以提高程序的執(zhí)行效率和性能,通過對本文的閱讀,讀者可以更好地理解Golang內(nèi)存對齊的原理和技巧,并應(yīng)用于實(shí)際編程中2023-08-08golang通過反射手動實(shí)現(xiàn)json序列化的方法
在 Go 語言中,JSON 序列化和反序列化通常通過標(biāo)準(zhǔn)庫 encoding/json 來實(shí)現(xiàn),本文給大家介紹golang 通過反射手動實(shí)現(xiàn)json序列化的方法,感興趣的朋友一起看看吧2024-12-12