Golang的sort庫如何自定義排序規(guī)則 實(shí)現(xiàn)Interface接口實(shí)例

要自定義golang的排序規(guī)則,核心在于實(shí)現(xiàn)sort.interface接口并定義其三個(gè)方法。1. len()返回元素?cái)?shù)量;2. less(i, j int) bool定義排序邏輯,先按年齡升序,若相同則按名字字母順序;3. swap(i, j int)交換元素位置。只要結(jié)構(gòu)體實(shí)現(xiàn)了這三個(gè)方法,即可通過sort.sort()進(jìn)行排序,適用于多字段復(fù)合排序場(chǎng)景,如按category升序、price降序和creationdate升序等。

Golang的sort庫如何自定義排序規(guī)則 實(shí)現(xiàn)Interface接口實(shí)例

自定義golang的sort庫,實(shí)現(xiàn)你想要的排序規(guī)則,核心在于實(shí)現(xiàn)sort.Interface接口。這個(gè)接口定義了三個(gè)方法:Len(), Less(i, j int) bool, 和 Swap(i, j int)。只要你的數(shù)據(jù)類型實(shí)現(xiàn)了這三個(gè)方法,就可以直接傳入sort.Sort()函數(shù)進(jìn)行排序。

Golang的sort庫如何自定義排序規(guī)則 實(shí)現(xiàn)Interface接口實(shí)例

解決方案

假設(shè)我們有一個(gè)自定義的結(jié)構(gòu)體Person,包含Name和Age字段,我們想根據(jù)年齡從小到大排序,如果年齡相同則根據(jù)名字字母順序排序。

Golang的sort庫如何自定義排序規(guī)則 實(shí)現(xiàn)Interface接口實(shí)例

package main  import (     "fmt"     "sort" )  // Person 定義了一個(gè)簡(jiǎn)單的結(jié)構(gòu)體 type Person struct {     Name String     Age  int }  // ByAge 實(shí)現(xiàn)了 sort.Interface 接口 type ByAge []Person  func (a ByAge) Len() int {     return len(a) }  // Less 方法定義了排序的邏輯 // 如果年齡不同,按年齡升序;如果年齡相同,按名字字母升序 func (a ByAge) Less(i, j int) bool {     if a[i].Age != a[j].Age {         return a[i].Age < a[j].Age     }     return a[i].Name < a[j].Name }  func (a ByAge) Swap(i, j int) {     a[i], a[j] = a[j], a[i] }  func main() {     people := []Person{         {"Alice", 30},         {"Bob", 25},         {"Charlie", 30},         {"David", 25},         {"Eve", 35},     }      fmt.Println("排序前:", people)      // 使用 sort.Sort() 進(jìn)行排序     sort.Sort(ByAge(people))      fmt.Println("排序后:", people)      // 另一個(gè)例子:如果我想反向排序,或者只按名字排序呢?     // 可以再定義一個(gè)實(shí)現(xiàn)了 sort.Interface 的類型     type ByName []Person     func (a ByName) Len() int { return len(a) }     func (a ByName) Less(i, j int) bool { return a[i].Name < a[j].Name }     func (a ByName) Swap(i, j int) { a[i], a[j] = a[j], a[i] }      people2 := []Person{         {"Alice", 30},         {"Bob", 25},         {"Charlie", 30},         {"David", 25},         {"Eve", 35},     }     fmt.Println("n按名字排序前:", people2)     sort.Sort(ByName(people2))     fmt.Println("按名字排序后:", people2) }

Golang sort.Interface:為何它是自定義排序的核心?

初次接觸Go的sort庫,你可能會(huì)覺得它有點(diǎn)“不那么直接”。不像一些語言提供了可以直接傳入匿名函數(shù)或Lambda表達(dá)式的排序方法,Go的sort.Sort()要求你傳入一個(gè)實(shí)現(xiàn)了特定接口的類型。這背后其實(shí)是go語言設(shè)計(jì)哲學(xué)的一個(gè)體現(xiàn):通過接口實(shí)現(xiàn)多態(tài)和通用性。

立即學(xué)習(xí)go語言免費(fèi)學(xué)習(xí)筆記(深入)”;

sort.Interface正是這種哲學(xué)在排序領(lǐng)域的具體應(yīng)用。它沒有直接對(duì)具體的數(shù)據(jù)類型(比如[]int或[]string)進(jìn)行操作,而是定義了一套“行為契約”:告訴我你的長(zhǎng)度,告訴我如何比較兩個(gè)元素,告訴我如何交換兩個(gè)元素。只要任何類型滿足了這三個(gè)契約,sort.Sort()就能對(duì)其進(jìn)行排序,而無需關(guān)心它到底是什么數(shù)據(jù)類型。這種設(shè)計(jì)避免了Go語言在核心庫中引入泛型(在Go 1.18之前),卻依然能實(shí)現(xiàn)強(qiáng)大的通用排序功能。它強(qiáng)制你思考你的數(shù)據(jù)結(jié)構(gòu)如何與排序算法交互,而不是僅僅提供一個(gè)黑盒。對(duì)我個(gè)人而言,這種“顯式”的接口實(shí)現(xiàn)方式,讓排序邏輯變得非常清晰和可控,特別是當(dāng)你處理復(fù)雜數(shù)據(jù)結(jié)構(gòu)時(shí),這種控制力顯得尤為重要。

Golang的sort庫如何自定義排序規(guī)則 實(shí)現(xiàn)Interface接口實(shí)例

深入理解Golang sort.Interface:Len(), Less(), Swap() 各自扮演什么角色?

這三個(gè)方法是sort.Interface的基石,每一個(gè)都不可或缺,它們共同協(xié)作,讓sort.Sort()這個(gè)“幕后工作者”能夠高效地完成任務(wù)。

  • Len() int: 這個(gè)方法很簡(jiǎn)單,它返回集合中元素的數(shù)量。對(duì)于sort.Sort()內(nèi)部的排序算法來說,這是它了解數(shù)據(jù)規(guī)模的第一步。沒有這個(gè),算法就不知道它要處理多少個(gè)元素,也就無從談起排序邊界。你可以把它想象成告訴一個(gè)機(jī)器人:“這里有X個(gè)箱子需要整理。”

  • Less(i, j int) bool: 這是整個(gè)自定義排序規(guī)則的“大腦”。它接收兩個(gè)索引i和j,然后你需要返回一個(gè)布爾值,表示索引i處的元素是否應(yīng)該排在索引j處的元素前面。true意味著i應(yīng)該在j之前,false則相反。所有的排序邏輯,無論是升序、降序、多字段排序,還是根據(jù)自定義權(quán)重排序,都體現(xiàn)在這個(gè)方法里。它的實(shí)現(xiàn)決定了最終的排序結(jié)果。舉個(gè)例子,如果你想降序,你可能寫return a[i] > a[j]。這個(gè)方法被調(diào)用得非常頻繁,所以它的效率直接影響了整個(gè)排序的性能。

  • Swap(i, j int): 這個(gè)方法負(fù)責(zé)交換集合中索引i和j處的元素。當(dāng)排序算法根據(jù)Less方法的判斷需要調(diào)整元素位置時(shí),它就會(huì)調(diào)用Swap。這個(gè)操作必須是原子的,并且正確地將兩個(gè)元素在底層數(shù)據(jù)結(jié)構(gòu)中進(jìn)行互換。如果Swap實(shí)現(xiàn)有誤,排序結(jié)果必然是錯(cuò)誤的,甚至可能導(dǎo)致程序崩潰。它是排序算法實(shí)際“移動(dòng)”數(shù)據(jù)的雙手。

這三個(gè)方法協(xié)同工作,Len定義了范圍,Less定義了比較規(guī)則,Swap執(zhí)行了實(shí)際的重排。它們共同為sort.Sort提供了一個(gè)抽象層,使其能夠?qū)θ魏螌?shí)現(xiàn)了sort.Interface的數(shù)據(jù)進(jìn)行排序,而無需知道數(shù)據(jù)的具體類型。

實(shí)際場(chǎng)景:不僅僅是數(shù)字和字符串,如何用sort.Interface對(duì)復(fù)雜結(jié)構(gòu)體進(jìn)行多字段排序?

在實(shí)際開發(fā)中,我們很少僅僅排序簡(jiǎn)單的數(shù)字或字符串切片。更多時(shí)候,我們需要對(duì)包含多個(gè)字段的復(fù)雜結(jié)構(gòu)體切片進(jìn)行排序,而且排序規(guī)則可能涉及多個(gè)字段的優(yōu)先級(jí)。這就是sort.Interface真正展現(xiàn)其強(qiáng)大之處的地方。

以上面的Person結(jié)構(gòu)體為例,我們已經(jīng)展示了如何根據(jù)Age和Name進(jìn)行復(fù)合排序。這種多字段排序的核心思想,就是在Less方法中實(shí)現(xiàn)一個(gè)“級(jí)聯(lián)判斷”:

  1. 優(yōu)先級(jí)最高的字段:首先比較優(yōu)先級(jí)最高的字段。如果它們不相等,那么直接根據(jù)這個(gè)字段的結(jié)果返回。
  2. 次優(yōu)先級(jí)字段:如果優(yōu)先級(jí)最高的字段相等,那么才繼續(xù)比較下一個(gè)優(yōu)先級(jí)的字段。
  3. 依此類推:直到所有需要比較的字段都被檢查過,或者找到一個(gè)不相等的字段。

這種模式非常靈活,你可以根據(jù)業(yè)務(wù)需求,自由地組合任意數(shù)量的字段進(jìn)行排序。比如,你可能有一個(gè)Product結(jié)構(gòu)體,需要先按Category排序,然后按Price降序,最后按CreationDate升序。在Less方法中,你只需要層層遞進(jìn)地寫if判斷即可。

// 假設(shè)有Product結(jié)構(gòu)體 type Product struct {     Category string     Price    float64     CreationDate time.Time // 假設(shè)這是一個(gè)time.Time類型 }  // ByComplexProductCriteria 實(shí)現(xiàn)了 sort.Interface type ByComplexProductCriteria []Product  func (p ByComplexProductCriteria) Len() int { return len(p) } func (p ByComplexProductCriteria) Less(i, j int) bool {     // 1. 先按Category升序     if p[i].Category != p[j].Category {         return p[i].Category < p[j].Category     }     // 2. 如果Category相同,再按Price降序     if p[i].Price != p[j].Price {         return p[i].Price > p[j].Price // 注意這里是 > 實(shí)現(xiàn)降序     }     // 3. 如果Category和Price都相同,最后按CreationDate升序     return p[i].CreationDate.Before(p[j].CreationDate) } func (p ByComplexProductCriteria) Swap(i, j int) { p[i], p[j] = p[j], p[i] }

這種模式確保了排序的穩(wěn)定性和可預(yù)測(cè)性。當(dāng)然,在Go 1.8以后,sort.Slice和sort.SliceStable提供了更簡(jiǎn)潔的寫法,允許你直接傳入一個(gè)匿名函數(shù)作為比較器,避免了為每個(gè)排序規(guī)則定義一個(gè)新類型。然而,理解sort.Interface的原理依然是基礎(chǔ),尤其是在你需要?jiǎng)?chuàng)建可復(fù)用的、封裝好的排序邏輯時(shí),或者在一些老舊代碼庫中看到這種模式時(shí),你都能游刃有余。它也讓我們更深入地思考,Go語言是如何通過接口這種輕量級(jí)的抽象機(jī)制,實(shí)現(xiàn)如此強(qiáng)大的通用能力的。

? 版權(quán)聲明
THE END
喜歡就支持一下吧
點(diǎn)贊7 分享