在 Go 1.21.0 版本中,引入了 切片泛型庫,它提供了很多有用的函數,特別是在搜索、查找和排序等方面,為我們開發者提供了諸多便利之處。而本文將會對 slices 庫提供的函數進行介紹,準備好了嗎,準備一杯你喜歡的咖啡或茶,隨著本文一探究竟吧。
slices 庫包含的函數可以分為以下類型:
BinarySearch 函數用于在有序的切片中 查找 目標元素,并返回其在切片中的 位置。該函數有兩個返回值,第一個是指定元素的下標索引,第二個是一個 bool 值,表示是否在切片中找到指定元素。
下面是使用該函數的一個例子:
// https://github.com/chenmingyong0423/blog/blob/master/tutorial-code/slices/binary_search/binary_search.gopackage mainimport ( "fmt" "slices")func main() { scores := []int{70, 85, 90, 95, 98, 99, 100} idx, b := slices.BinarySearch(scores, 80) fmt.Println(idx, b) idx, b = slices.BinarySearch(scores, 95) fmt.Println(idx, b)}
程序運行結果如下所示:
1 false3 true
BinarySearchFunc 功能和 BinarySearch 類似,但它更加靈活,在它接收的參數里,其中有一個是 cmp 比較函數,通過該函數我們可以為任何的數據結構定義比較邏輯。
cmp 比較函數的介紹如下:
cmp func(E, T) int
當 E 的位置在 T 之前,返回負數;當前 E 等于 T 時,應返回 0,當 E 的位置在 T 的后面時,返回正數。
下面是使用該函數的一個例子:
// https://github.com/chenmingyong0423/blog/blob/master/tutorial-code/slices/binary_search/binary_search_func.gopackage mainimport ( "cmp" "fmt" "slices")func main() { type User struct { Name string Age int } users := []User{ {"Aaron", 20}, {"Gopher", 24}, {"Harry", 18}, } idx, b := slices.BinarySearchFunc(users, User{Name: "Gopher"}, func(src User, dst User) int { return cmp.Compare(src.Name, dst.Name) }) fmt.Println("Gopher:", idx, b)}
在比較函數里,如果不是要實現特別復雜的比較,我們完全可以使用 cmp 包提供的 Compare 函數。
程序運行結果如下所示:
Gopher: 1 true
Clip 函數用于刪除切片中未使用的容量,執行操作后,切片的長度 = 切片的容量。
// https://github.com/chenmingyong0423/blog/blob/master/tutorial-code/slices/clip/clip.gopackage mainimport ( "fmt" "slices")func main() { s := make([]int, 0, 8) s = append(s, 1, 2, 3, 4) fmt.Printf("len: %d, cap: %d/n", len(s), cap(s)) s = slices.Clip(s) fmt.Printf("len: %d, cap: %d/n", len(s), cap(s))}
程序運行結果如下所示:
len: 4, cap: 8len: 4, cap: 4
Clone 函數返回一個拷貝的切片副本,元素是賦值復制,因此是淺拷貝。
// https://github.com/chenmingyong0423/blog/blob/master/tutorial-code/slices/clone/clone.gopackage mainimport ( "fmt" "slices")func main() { type User struct { Name string } s := []*User{{Name: "Gopher"}} copiedSlice := slices.Clone(s) copiedSlice[0].Name = "陳明勇" fmt.Println(s[0].Name == copiedSlice[0].Name) // true}
由于是淺拷貝,修改副本切片里的元素,原切片的元素也會更新。
Compact 函數會將切片里連續的相同元素替換為一個元素。
// https://github.com/chenmingyong0423/blog/blob/master/tutorial-code/slices/compact/compact.gopackage mainimport ( "fmt" "slices")func main() { s := []int{1, 2, 2, 3, 3, 4, 5} newSlice := slices.Compact(s) fmt.Println(newSlice)}
程序運行結果如下所示:
[1 2 3 4 5]
Compact 的原理是通過移動元素來合并重復項。盡管處理后的切片長度減少了,但其底層數組的實際值仍然包括被“拋棄”的元素,例如 [1, 2, 3, 4, 5, 4, 5]。這些尾部的元素 [4, 5] 雖不在新切片中,但仍占用內存。特別是當元素為指針時,這些元素會阻止它們所引用的對象被垃圾回收。為確保這些對象可以被回收,我們應該考慮將這些元素置為 nil。
CompactFunc 和 Compact 函數功能類似,但它使用一個相等性函數來比較元素。
案例:相同元素合并為一個,比較元素時,忽略大小寫
// https://github.com/chenmingyong0423/blog/blob/master/tutorial-code/slices/compact/compact_func.gopackage mainimport ( "fmt" "slices" "strings")func main() { names := []string{"cmy", "CmY", "Gopher", "GOPHER", "Jack"} names = slices.CompactFunc(names, func(a, b string) bool { return strings.ToLower(a) == strings.ToLower(b) }) fmt.Println(names)}
程序運行結果如下所示:
[cmy Gopher Jack]
Compare 函數是一個比較函數,內部使用 cmp 包的 Compare 函數對 s1 和 s2 的每對元素進行比較。元素按順序從索引 0 開始進行比較,直到有一對元素不相等。返回第一對不匹配元素的比較結果。如果兩個切片在某一個切片結束之前都保持相等,那么長度較短的切片被認為小于較長的切片。
如果 s1 == s2,結果為 0;如果 s1 < s2,結果為 -1;如果 s1 > s2,結果為 +1。
// https://github.com/chenmingyong0423/blog/blob/master/tutorial-code/slices/compare/compare.gopackage mainimport ( "fmt" "slices")func main() { names := []string{"Aaron", "Bob", "Gopher"} fmt.Println("相等: ", slices.Compare(names, []string{"Aaron", "Bob", "Gopher"})) fmt.Println("G < F, 第一個的切片小于第二個的切片:", slices.Compare(names, []string{"Aaron", "Bob", "Frida"})) fmt.Println("G > H, 第一個的切片大于第二個的切片:", slices.Compare(names, []string{"Aaron", "Bob", "Harry"})) fmt.Println("3 > 2, 第一個的切片大于第二個的切片:", slices.Compare(names, []string{"Aaron", "Bob"}))}
程序運行結果如下所示:
相等: 0G < F, 第一個的切片小于第二個的切片: 1G > H, 第一個的切片大于第二個的切片: -13 > 2, 第一個的切片大于第二個的切片: 1
CompareFunc 和 Compare 函數的功能類似,但它對每對元素使用自定義的比較函數進行比較。比較函數在 BinarySearchFunc 小節里已經介紹過,這里就不多介紹。
案例:使用自定義的比較函數來比較兩個切片中的元素,此比較函數基于字符串的長度而不是字典順序。比較規則是:更短的字符串被認為是較小的。
// https://github.com/chenmingyong0423/blog/blob/master/tutorial-code/slices/compare/compare_func.gopackage mainimport ( "fmt" "slices")func main() { s1 := []string{"apple", "banana", "cherry"} s2 := []string{"apple", "blueberry", "date"} result := slices.CompareFunc(s1, s2, func(s string, s2 string) int { iflen(s) < len(s2) { return-1 } elseiflen(s) > len(s2) { return1 } return0 }) fmt.Println("第一個切片比第二個切片小:", result) // -1}
程序運行結果如下所示:
第一個切片比第二個切片小: -1
Contains 函數用于判斷切片里是否包含指定元素。
// https://github.com/chenmingyong0423/blog/blob/master/tutorial-code/slices/contains/contains.gopackage mainimport ( "fmt" "slices")func main() { numbers := []int{1, 5, -1, 3, 2} hasNegativeOne := slices.Contains(numbers, -1) fmt.Println("包含 -1:", hasNegativeOne)}
程序運行結果如下所示:
包含 -1: true
ContainsFunc 和 Contains 函數功能類似,但它使用一個相等性函數來確定被包含的元素。
例如我們要在一個切片中判斷是否包含負數元素:
// https://github.com/chenmingyong0423/blog/blob/master/tutorial-code/slices/contains/contains_func.gopackage mainimport ( "fmt" "slices")func main() { numbers := []int{1, 5, -1, 3, 2} containNegative := slices.ContainsFunc(numbers, func(i int) bool { return i < 0 }) fmt.Println("包含負數:", containNegative)}
程序運行結果如下所示:
包含負數: true
Delete 函數的功能是從指定切片 s 中刪除指定范圍 s[i:j] 的元素,并返回新的的切片。
使用注意事項:
下面是使用該函數的一個例子:
// https://github.com/chenmingyong0423/blog/blob/master/tutorial-code/slices/delete/delete.gopackage mainimport ( "fmt" "slices")func main() { numbers := []int{1, 2, 3, 4, 5} newNumbers := slices.Delete(numbers, 1, 3) fmt.Println(newNumbers)}
程序運行結果如下所示:
[1 4 5]
刪除位置范圍 1 ~ 3 的元素,不包含位置 3。
DeleteFunc 和 Delete 函數功能類似,但它使用一個相等性函數來確定需要刪除的元素。
案例:從切片中刪除奇數元素
// https://github.com/chenmingyong0423/blog/blob/master/tutorial-code/slices/delete/delete_func.gopackage mainimport ( "fmt" "slices")func main() { numbers := []int{1, 2, 3, 4, 5} newNumbers := slices.DeleteFunc(numbers, func(i int) bool { return i%2 != 0 }) fmt.Println(newNumbers)}
程序運行結果如下所示:
[2 4]
Equal 函數用于比較兩個切片是否相等,要求切片的元素類型必須是可比較(comparable)的。 其工作原理如下:
首先檢查兩個切片的長度,如果長度不同,則直接返回 false,表示這兩個切片不相等。如果長度相同,函數會逐個比較元素,按照遞增的順序進行比較。需要注意的是,對于浮點數,函數會忽略 NaN 值,不將其視為相等。
// https://github.com/chenmingyong0423/blog/blob/master/tutorial-code/slices/equal/equal.gopackage mainimport ( "fmt" "slices")func main() { numbers := []int{0, 1, 2} fmt.Println(slices.Equal(numbers, []int{0, 1, 2})) fmt.Println(slices.Equal(numbers, []int{3}))}
程序運行結果如下所示:
truefalse
EqualFunc 和 Equal 函數功能類似,但它使用一個相等性函數來比較元素。
案例:忽略大小寫比較
// https://github.com/chenmingyong0423/blog/blob/master/tutorial-code/slices/equal/equal_func.gopackage mainimport ( "fmt" "slices" "strings")func main() { names := []string{"cmy", "Gopher"} equal := slices.EqualFunc(names, []string{"CMY", "GOPHER"}, func(s string, s2 string) bool { return strings.ToLower(s) == strings.ToLower(s2) }) fmt.Println(equal)}
程序運行結果如下所示:
true
Grow 函數會根據需要增加切片的容量,以確保可以容納另外 n 個元素。在調用 Grow(n) 后,至少可以追加 n 個元素到切片中而無需再次分配內存。如果 n 為負數或者需要分配的內存太大,Grow 會引發異常。
// https://github.com/chenmingyong0423/blog/blob/master/tutorial-code/slices/grow/grow.gopackage mainimport ( "fmt" "slices")func main() { s := make([]int, 4, 5) fmt.Printf("len=%d, cap=%d/n", len(s), cap(s)) grow := slices.Grow(s, 4) fmt.Printf("len=%d, cap=%d/n", len(grow), cap(grow))}
程序運行結果如下所示:
len=4, cap=5len=4, cap=10
在調用 Grow 函數擴容之前,切片 s 可用容量只有 1,在擴容之后,可用容量為 6,可確保能至少能容納 4 個元素。
Index 函數返回指定元素在切片里第一次出現的下標索引值,如果元素不存在,則返回 -1 。
// https://github.com/chenmingyong0423/blog/blob/master/tutorial-code/slices/index/index.gopackage mainimport ( "fmt" "slices")func main() { numbers := []int{0, 1, 2} fmt.Println("找到元素位置:", slices.Index(numbers, 2)) fmt.Println("未找到元素位置:", slices.Index(numbers, 3))}
程序運行結果如下所示:
找到元素位置: 2未找到元素位置: -1
IndexFunc 和 Index 函數功能類似,但它使用一個相等性函數來比較元素。
// https://github.com/chenmingyong0423/blog/blob/master/tutorial-code/slices/index/index_func.gopackage mainimport ( "fmt" "slices")func main() { numbers := []int{1, 5, -1, 3, 2} idx := slices.IndexFunc(numbers, func(i int) bool { return i < 0 }) fmt.Println("負數的索引:", idx)}
程序運行結果如下所示:
負數的索引: 2
Insert 函數用于在一個切片 s 中的指定位置 i 處插入一組值 v...,然后返回修改后的切片。如果指定的索引 i 越界了,則會發生錯誤。
// https://github.com/chenmingyong0423/blog/blob/master/tutorial-code/slices/insert/insert.gopackage mainimport ( "fmt" "slices")func main() { numbers := []int{1, 3, 4} numbers = slices.Insert(numbers, 1, 2) numbers = slices.Insert(numbers, len(numbers), 5, 6) fmt.Println(numbers)}
程序運行結果如下所示:
[1 2 3 4 5 6]
IsSorted 函數用于判斷切片是按升序排列。
// https://github.com/chenmingyong0423/blog/blob/master/tutorial-code/slices/is_sorted/is_sorted.gopackage mainimport ( "fmt" "slices")func main() { fmt.Println("是升序排列:", slices.IsSorted([]int{1, 2, 3, 4, 5})) fmt.Println("不是升序排列:", slices.IsSorted([]int{1, 2, 3, 5, 4}))}
程序運行結果如下所示:
是升序排列: true不是升序排列: false
IsSortedFunc 和 IsSorted 函數功能類似,但它對每對元素使用自定義的比較函數進行比較。比較函數在 BinarySearchFunc 小節里已經介紹過,這里就不多介紹。
// https://github.com/chenmingyong0423/blog/blob/master/tutorial-code/slices/is_sorted/is_sorted_func.gopackage mainimport ( "cmp" "fmt" "slices" "strings")func main() { names := []string{"aaron", "Bob", "GOPHER"} isSortedInsensitive := slices.IsSortedFunc(names, func(a, b string) int { return cmp.Compare(strings.ToLower(a), strings.ToLower(b)) }) fmt.Println("是升序排列:", isSortedInsensitive) fmt.Println("不是升序排列:", slices.IsSorted(names))}
程序運行結果如下所示:
是升序排列: true不是升序排列: false
Max 函數返回切片中最大的元素,如果切片為空,則 panic。對于浮點數類型,如果切片中包含 NaN(非數字)值,那么結果將是 NaN。 NaN 是一種特殊的浮點數值,表示不是一個數字或無效數字。如果切片包含 NaN,那么最大值也將是 NaN,這是因為 NaN 不可比較大小。
// https://github.com/chenmingyong0423/blog/blob/master/tutorial-code/slices/max/max.gopackage mainimport ( "fmt" "slices")func main() { fmt.Println("最大的元素:", slices.Max([]int{1, 2, 5, 3, 4}))}
程序運行結果如下所示:
最大的元素: 5
MaxFunc 和 Max 函數功能類似,但它使用一個相等性函數來比較元素。
// https://github.com/chenmingyong0423/blog/blob/master/tutorial-code/slices/max/max_func.gopackage mainimport ( "cmp" "fmt" "slices")func main() { type User struct { Name string Age int } users := []User{ {"Aaron", 20}, {"Gopher", 24}, {"Harry", 18}, } maxUser := slices.MaxFunc(users, func(a, b User) int { return cmp.Compare(a.Age, b.Age) }) fmt.Println("最大的元素:", maxUser)}
程序運行結果如下所示:
最大的元素: {Gopher 24}
Min 函數返回切片中最小的元素,如果切片為空,則 panic。對于浮點數類型,如果切片中包含 NaN(非數字)值,那么結果將是 NaN。 NaN 是一種特殊的浮點數值,表示不是一個數字或無效數字。如果切片包含 NaN,那么最小值也將是 NaN,這是因為 NaN 不可比較大小。
// https://github.com/chenmingyong0423/blog/blob/master/tutorial-code/slices/min/min.gopackage mainimport ( "fmt" "slices")func main() { fmt.Println("最小的元素:", slices.Max([]int{1, 2, 5, 3, 4}))}
程序運行結果如下所示:
最小的元素: 1
MaxFunc 和 Max 函數功能類似,但它使用一個相等性函數來比較元素。
// https://github.com/chenmingyong0423/blog/blob/master/tutorial-code/slices/min/min_func.gopackage mainimport ( "cmp" "fmt" "slices")func main() { type User struct { Name string Age int } users := []User{ {"Aaron", 20}, {"Gopher", 24}, {"Harry", 18}, } maxUser := slices.MaxFunc(users, func(a, b User) int { return cmp.Compare(a.Age, b.Age) }) fmt.Println("最小的元素:", maxUser)}
程序運行結果如下所示:
最小的元素: {Harry 18}
Replace 函數用于將切片s 中的元素 s[i:j] 替換為給定的元素組 v,然后返回修改后的切片。如果 s[i:j] 不是 s 的有效切片范圍,函數會引發 panic。
// https://github.com/chenmingyong0423/blog/blob/master/tutorial-code/slices/replace/replace.gopackage mainimport ( "fmt" "slices")func main() { numbers := []int{1, 0, 0, 5} numbers = slices.Replace(numbers, 1, 3, 2, 3, 4) fmt.Println(numbers)}
程序運行結果如下所示:
[1 2 3 4 5]
Reverse 函數用于反轉切片中的元素,在給定切片里將元素的順序顛倒過來,而不會創建新的切片。
// https://github.com/chenmingyong0423/blog/blob/master/tutorial-code/slices/reverse/reverse.gopackage mainimport ( "fmt" "slices")func main() { numbers := []int{1, 2, 3, 4} slices.Reverse(numbers) fmt.Println(numbers)}
程序運行結果如下所示:
[4 3 2 1]
Sort 函數用于對切片中的元素進行升序排序。當對浮點數進行排序時,NaN 值會被排在其他值的前面。這意味著在排序浮點數時,NaN 值會被視為最小值,排在結果的最前面。
// https://github.com/chenmingyong0423/blog/blob/master/tutorial-code/slices/sort/sort.gopackage mainimport ( "fmt" "math" "slices")func main() { ints := []int{1, 2, 5, 3, 4} slices.Sort(ints) floats := []float64{2.0, 3.0, math.NaN(), 1.0} slices.Sort(floats) fmt.Println(ints) fmt.Println(floats)}
程序運行結果如下所示:
[1 2 3 4 5][NaN 1 2 3]
SortFunc 和 Sort 函數功能類似,但它對每對元素使用自定義的比較函數進行比較。比較函數在 BinarySearchFunc 小節里已經介紹過,這里就不多介紹。
// https://github.com/chenmingyong0423/blog/blob/master/tutorial-code/slices/sort/sort_func.gopackage mainimport ( "cmp" "fmt" "slices" "strings")func main() { names := []string{"Bob", "Aaron", "GOPHER"} slices.SortFunc(names, func(a, b string) int { return cmp.Compare(strings.ToLower(a), strings.ToLower(b)) }) fmt.Println(names)}
程序運行結果如下所示:
[1 2 3 4 5][NaN 1 2 3]
SortStableFunc 和 SortFunc 函數功能類似,但它進行的是穩定排序,它會保持相等元素的原始順序。
定排序意味著當有多個相等的元素時,它們的相對順序在排序后會保持不變。例如,如果有兩個元素 A 和 B,它們的值相等,且在原始切片中 A 出現在 B 之前,那么在排序后 A仍然會出現在 B 之前,不會改變它們的相對位置。
// https://github.com/chenmingyong0423/blog/blob/master/tutorial-code/slices/sort/sort_stable_func.gopackage mainimport ( "cmp" "fmt" "slices")func main() { type User struct { Name string Age int } users := []User{ {"Aaron", 20}, {"Gopher", 16}, {"Harry", 16}, {"Burt", 18}, } slices.SortStableFunc(users, func(a, b User) int { return cmp.Compare(a.Age, b.Age) }) fmt.Println(users)}
程序運行結果如下所示:
[{Gopher 16} {Harry 16} {Burt 18} {Aaron 20}]
排序之前,Harry 在 Gopher 后面,排序之后,也是同樣的相對位置。
本文全面介紹了 Go slices 庫的所有函數,并著重指出了使用某些函數時的注意事項,通過閱讀本文,相信你將能夠熟練掌握如何使用 Go Slices 庫。
本文中涉及到的相關代碼,都已上傳至:github.com/chenmingyong0423/blog/tree/master/tutorial-code/slices
本文轉載自微信公眾號「Go技術干貨」,可以通過以下二維碼關注。轉載本文請聯系Go技術干貨公眾號。
本文鏈接:http://www.www897cc.com/showinfo-26-16839-0.html玩轉 Go Slices 切片泛型庫
聲明:本網頁內容旨在傳播知識,若有侵權等問題請及時與本網聯系,我們將在第一時間刪除處理。郵件:2376512515@qq.com
上一篇: Go異步任務解決方案:Asynq