天天看點

Go1.21 :過了一年半,slices、maps 泛型庫終于加入标準庫

作者:Git風去雲往

在 2022 年 3 月,Go1.18 終于釋出。在該版本中,包含了 Go1.17 起就已存在的泛型,并于此版本正式釋出泛型特性。

泛型庫終于合進 master

曾經在 Go1.18 時,Go 語言之父 @Rob Pike 冒了個泡,掌了舵,讓不要這麼急把泛型重寫進标準庫。怕太着急,對泛型不熟會翻車。

如下圖:

Go1.21 :過了一年半,slices、maps 泛型庫終于加入标準庫

在經曆了一年半的等待後,最近 Go slices 和 maps 的泛型庫,終于被合并進 master 分支了。這意味着在 Go1.21 起,将會有泛型庫進入官方标準庫。

這相當于是個比較有标志性的節點了。

以下我們先看看一個簡單的泛型 Demo,再看看具體的 slices 和 maps 的泛型标準庫庫的 API 和使用方式。

泛型 Demo

以下是社群提供的一個泛型快速 Demo,可以跟着思考運作一下,看看自己泛型的基本使用掌握的如何。

代碼如下:

package main

import "fmt"

func MapKeys[K comparable, V any](m map[K]V) []K {
    r := make([]K, 0, len(m))
    for k := range m {
        r = append(r, k)
    }
    return r
}

type List[T any] struct {
    head, tail *element[T]
}

type element[T any] struct {
    next *element[T]
    val  T
}

func (lst *List[T]) Push(v T) {
    if lst.tail == nil {
        lst.head = &element[T]{val: v}
        lst.tail = lst.head
    } else {
        lst.tail.next = &element[T]{val: v}
        lst.tail = lst.tail.next
    }
}

func (lst *List[T]) GetAll() []T {
    var elems []T
    for e := lst.head; e != nil; e = e.next {
        elems = append(elems, e.val)
    }
    return elems
}

func main() {
    var m = map[int]string{1: "2", 2: "4", 4: "8"}

    fmt.Println("keys:", MapKeys(m))

    _ = MapKeys[int, string](m)

    lst := List[int]{}
    lst.Push(10)
    lst.Push(13)
    lst.Push(23)
    fmt.Println("list:", lst.GetAll())
}
           

輸出結果:

keys: [4 1 2]
list: [10 13 23]
           

泛型 slices

以下給大家介紹泛型 slices 庫的 API 和對應的用法。如果有看源碼的興趣,可以檢視 src/slices/slices.go 檔案。

其包含如下方法:

func BinarySearch[E constraints.Ordered](x []E, target E) (int, bool)
func BinarySearchFunc[E, T any](x []E, target T, cmp func(E, T) int) (int, bool)
           
  • BinarySearch:在已排序的切片中搜尋目标,并傳回找到目标的位置,或者目标在排序順序中出現的位置;函數會傳回一個 bool 值,表示是否真的在切片中找到目标。切片必須按遞增順序排序。
  • BinarySearchFunc:同上類似用法,差別在于可以傳自己定義的比較函數。
func Clip[S ~[]E, E any](s S) S
func Clone[S ~[]E, E any](s S) S
func Compact[S ~[]E, E comparable](s S) S
func CompactFunc[S ~[]E, E any](s S, eq func(E, E) bool) S
func Compare[E constraints.Ordered](s1, s2 []E) int
func CompareFunc[E1, E2 any](s1 []E1, s2 []E2, cmp func(E1, E2) int) int
           
  • Clip:從切片中删除未使用的容量,傳回 s[:len(s):len(s)]。
  • Clone:拷貝切片的副本,切片元素是使用指派複制的,是淺拷貝。
  • Compact:将連續運作的相等元素替換為單個副本。類似于 Unix 的 uniq 指令。該函數會直接修改切片的元素,它不會建立新切片。
  • CompactFunc:同上類似用法,差別在于可傳自定義函數進行比較。
func Contains[E comparable](s []E, v E) bool
func ContainsFunc[E any](s []E, f func(E) bool) bool
func Delete[S ~[]E, E any](s S, i, j int) S
           
  • Contains:在切片中查找所傳入的參數是否存在,傳回一個 bool 值。
  • ContainsFunc:同上,可傳自定義函數。
  • Delete:從切片中删除元素 s[i:j],傳回被修改(删除元素)後的切片。
func Equal[E comparable](s1, s2 []E) bool
func EqualFunc[E1, E2 any](s1 []E1, s2 []E2, eq func(E1, E2) bool) bool
func Grow[S ~[]E, E any](s S, n int) S
           
  • Equal:檢查兩個所傳入的切片是否相等,需要確定長度相同,所有元素相等。如果長度不同,也是會傳回 false。
  • EqualFunc:同上,可傳自定義函數。
  • Grow:增加切片的容量,至少增加 n 個元素的空間。如果 n 是負數或者太大,無法配置設定記憶體,就會導緻産生 panic。
func Index[E comparable](s []E, v E) int
func IndexFunc[E any](s []E, f func(E) bool) int
func Insert[S ~[]E, E any](s S, i int, v ...E) S
func Replace[S ~[]E, E any](s S, i, j int, v ...E) S
           
  • Index:傳回所需檢查元素在切片中第一次出現的索引位置。如果不存在,則傳回 -1。
  • IndexFunc:同上,可傳自定義函數。
  • Replace:用所傳入的參數替換對應的元素,并傳回修改後的切片。
func IsSorted[E constraints.Ordered](x []E) bool
func IsSortedFunc[E any](x []E, less func(a, b E) bool) bool
func Sort[E constraints.Ordered](x []E)
func SortFunc[E any](x []E, less func(a, b E) bool)
func SortStableFunc[E any](x []E, less func(a, b E) bool)
           
  • IsSorted:檢查所傳入的切片是否以升序排序。
  • IsSortedFunc:同上,可傳自定義函數。
  • Sort:按升序對任意有序類型的切片進行排序。
  • SortFunc:同上,可傳自定義函數。
  • SortStableFunc:對所傳入的切片進行排序,同時保持相等元素的原始順序,使用較少的元素進行比較。

泛型 maps

以下給大家介紹泛型庫的 API 和對應的用法。如果有看源碼的興趣,可以檢視 src/maps/maps.go 檔案。

其包含如下方法:

func Keys[M ~map[K]V, K comparable, V any](m M) []K
func Values[M ~map[K]V, K comparable, V any](m M) []V
func Equal[M1, M2 ~map[K]V, K, V comparable](m1 M1, m2 M2) bool
func EqualFunc[M1 ~map[K]V1, M2 ~map[K]V2, K comparable, V1, V2 any](m1 M1, m2 M2, eq func(V1, V2) bool) bool
           
  • Keys:傳回 map 的鍵值内容,鍵值将以不确定的順序出現。
  • Values:傳回 map 的值,值将以不确定的順序出現。
  • Equal:檢查兩個 map 是否包含相同的鍵/值對,内部會使用 == 來比較數值。
  • EqualFunc:EqualFunc與 Equal 方法類似,但使用閉包方法來比較數值,鍵值仍然用 == 來比較。
func DeleteFunc[M ~map[K]V, K comparable, V any](m M, del func(K, V) bool)
func Clear[M ~map[K]V, K comparable, V any](m M)
func Clone[M ~map[K]V, K comparable, V any](m M) M
func Copy[M1 ~map[K]V, M2 ~map[K]V, K comparable, V any](dst M1, src M2)
           
  • DeleteFunc:删除 map 中閉包方法傳回 true 的任何鍵/值對。
  • Clear:清除從 map 中删除所有條目,使之為空。
  • Clone:傳回一個 map 的副本,這是一個淺層克隆,新拷貝出來的的鍵和值使用普通的指派來設定。
  • Copy:複制 src 中的所有鍵/值對,并将其加入 dst。當 src 中的一個鍵已經存在于 dst 中時,dst 中的值将被與 src 中的鍵相關的值所覆寫。

總結

Go 語言加不加泛型,怎麼加泛型。吵了十多年,才把泛型這個新特性納入進來。又花了一年半的時間,才把标準庫最常見用的 slices、maps 泛型再逐漸納入進來。

雖然聽起來一切都是那麼的讓人激動。但你細數一下時間,其實是比較久的。等 Go 官方庫都能夠叱咤泛型,可能還需要相當一段的時間。

你在你的 Go 項目代碼中用上了嗎?

#挑戰30天在頭條寫日記#

繼續閱讀