日本综合一区二区|亚洲中文天堂综合|日韩欧美自拍一区|男女精品天堂一区|欧美自拍第6页亚洲成人精品一区|亚洲黄色天堂一区二区成人|超碰91偷拍第一页|日韩av夜夜嗨中文字幕|久久蜜综合视频官网|精美人妻一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時間:8:30-17:00
你可能遇到了下面的問題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營銷解決方案
創(chuàng)新互聯(lián)GO教程:Go語言排序(借助sort.Interface接口)

排序操作和字符串格式化一樣是很多程序經(jīng)常使用的操作。盡管一個最短的快排程序只要 15 行就可以搞定,但是一個健壯的實現(xiàn)需要更多的代碼,并且我們不希望每次我們需要的時候都重寫或者拷貝這些代碼。

公司主營業(yè)務(wù):網(wǎng)站制作、網(wǎng)站建設(shè)、移動網(wǎng)站開發(fā)等業(yè)務(wù)。幫助企業(yè)客戶真正實現(xiàn)互聯(lián)網(wǎng)宣傳,提高企業(yè)的競爭能力。創(chuàng)新互聯(lián)建站是一支青春激揚、勤奮敬業(yè)、活力青春激揚、勤奮敬業(yè)、活力澎湃、和諧高效的團隊。公司秉承以“開放、自由、嚴謹、自律”為核心的企業(yè)文化,感謝他們對我們的高要求,感謝他們從不同領(lǐng)域給我們帶來的挑戰(zhàn),讓我們激情的團隊有機會用頭腦與智慧不斷的給客戶帶來驚喜。創(chuàng)新互聯(lián)建站推出三穗免費做網(wǎng)站回饋大家。

幸運的是,sort 包內(nèi)置的提供了根據(jù)一些排序函數(shù)來對任何序列排序的功能。它的設(shè)計非常獨到。在很多語言中,排序算法都是和序列數(shù)據(jù)類型關(guān)聯(lián),同時排序函數(shù)和具體類型元素關(guān)聯(lián)。

相比之下,Go語言的 sort.Sort 函數(shù)不會對具體的序列和它的元素做任何假設(shè)。相反,它使用了一個接口類型 sort.Interface 來指定通用的排序算法和可能被排序到的序列類型之間的約定。這個接口的實現(xiàn)由序列的具體表示和它希望排序的元素決定,序列的表示經(jīng)常是一個切片。

一個內(nèi)置的排序算法需要知道三個東西:序列的長度,表示兩個元素比較的結(jié)果,一種交換兩個元素的方式;這就是 sort.Interface 的三個方法:

package sort
type Interface interface {
    Len() int            // 獲取元素數(shù)量
    Less(i, j int) bool // i,j是序列元素的指數(shù)。
    Swap(i, j int)        // 交換元素
}

為了對序列進行排序,我們需要定義一個實現(xiàn)了這三個方法的類型,然后對這個類型的一個實例應(yīng)用 sort.Sort 函數(shù)。思考對一個字符串切片進行排序,這可能是最簡單的例子了。下面是這個新的類型 MyStringList  和它的 Len,Less 和 Swap 方法

type MyStringList  []string
func (p MyStringList ) Len() int { return len(m) }
func (p MyStringList ) Less(i, j int) bool { return m[i] < m[j] }
func (p MyStringList ) Swap(i, j int) { m[i], m[j] = m[j], m[i] }

使用sort.Interface接口進行排序

對一系列字符串進行排序時,使用字符串切片([]string)承載多個字符串。使用 type 關(guān)鍵字,將字符串切片([]string)定義為自定義類型 MyStringList。為了讓 sort 包能識別 MyStringList,能夠?qū)?MyStringList 進行排序,就必須讓 MyStringList 實現(xiàn) sort.Interface 接口。

下面是對字符串排序的詳細代碼(代碼1):

package main

import (
    "fmt"
    "sort"
)

// 將[]string定義為MyStringList類型
type MyStringList []string

// 實現(xiàn)sort.Interface接口的獲取元素數(shù)量方法
func (m MyStringList) Len() int {
    return len(m)
}

// 實現(xiàn)sort.Interface接口的比較元素方法
func (m MyStringList) Less(i, j int) bool {
    return m[i] < m[j]
}

// 實現(xiàn)sort.Interface接口的交換元素方法
func (m MyStringList) Swap(i, j int) {
    m[i], m[j] = m[j], m[i]
}

func main() {

    // 準備一個內(nèi)容被打亂順序的字符串切片
    names := MyStringList{
        "3. Triple Kill",
        "5. Penta Kill",
        "2. Double Kill",
        "4. Quadra Kill",
        "1. First Blood",
    }

    // 使用sort包進行排序
    sort.Sort(names)

    // 遍歷打印結(jié)果
    for _, v := range names {
            fmt.Printf("%s\n", v)
    }

}

代碼輸出結(jié)果:

1. First Blood
2. Double Kill
3. Triple Kill
4. Quadra Kill
5. Penta Kill

代碼說明如下:

  • 第 9 行,接口實現(xiàn)不受限于結(jié)構(gòu)體,任何類型都可以實現(xiàn)接口。要排序的字符串切片 []string 是系統(tǒng)定制好的類型,無法讓這個類型去實現(xiàn) sort.Interface 排序接口。因此,需要將 []string 定義為自定義的類型。
  • 第 12 行,實現(xiàn)獲取元素數(shù)量的 Len() 方法,返回字符串切片的元素數(shù)量。
  • 第 17 行,實現(xiàn)比較元素的 Less() 方法,直接取 m 切片的 i 和 j 元素值進行小于比較,并返回比較結(jié)果。
  • 第 22 行,實現(xiàn)交換元素的 Swap() 方法,這里使用Go語言的多變量賦值特性實現(xiàn)元素交換。
  • 第 29 行,由于將 []string 定義成 MyStringList 類型,字符串切片初始化的過程等效于下面的寫法:
    names := []string {
        "3. Triple Kill",
        "5. Penta Kill",
        "2. Double Kill",
        "4. Quadra Kill",
        "1. First Blood",
    }
  • 第 38 行,使用 sort 包的 Sort() 函數(shù),將 names(MyStringList類型)進行排序。排序時,sort 包會通過 MyStringList 實現(xiàn)的 Len()、Less()、Swap() 這 3 個方法進行數(shù)據(jù)獲取和修改。
  • 第 41 行,遍歷排序好的字符串切片,并打印結(jié)果。

常見類型的便捷排序

通過實現(xiàn) sort.Interface 接口的排序過程具有很強的可定制性,可以根據(jù)被排序?qū)ο蟊容^復雜的特性進行定制。例如,需要多種排序邏輯的需求就適合使用 sort.Interface 接口進行排序。但大部分情況中,只需要對字符串、整型等進行快速排序。Go語言中提供了一些固定模式的封裝以方便開發(fā)者迅速對內(nèi)容進行排序。

1) 字符串切片的便捷排序

sort 包中有一個 StringSlice 類型,定義如下:

type StringSlice []string

func (p StringSlice) Len() int           { return len(p) }
func (p StringSlice) Less(i, j int) bool { return p[i] < p[j] }
func (p StringSlice) Swap(i, j int)      { p[i], p[j] = p[j], p[i] }

// Sort is a convenience method.
func (p StringSlice) Sort() { Sort(p) }

sort 包中的 StringSlice 的代碼與 MyStringList 的實現(xiàn)代碼幾乎一樣。因此,只需要使用 sort 包的 StringSlice 就可以更簡單快速地進行字符串排序。將代碼1中的排序代碼簡化后如下所示:

names := sort.StringSlice{
    "3. Triple Kill",
    "5. Penta Kill",
    "2. Double Kill",
    "4. Quadra Kill",
    "1. First Blood",
}

sort.Sort(names)

簡化后,只要兩句代碼就實現(xiàn)了字符串排序的功能。

2) 對整型切片進行排序

除了字符串可以使用 sort 包進行便捷排序外,還可以使用 sort.IntSlice 進行整型切片的排序。sort.IntSlice 的定義如下:

type IntSlice []int

func (p IntSlice) Len() int           { return len(p) }
func (p IntSlice) Less(i, j int) bool { return p[i] < p[j] }
func (p IntSlice) Swap(i, j int)      { p[i], p[j] = p[j], p[i] }

// Sort is a convenience method.
func (p IntSlice) Sort() { Sort(p) }

sort 包在 sort.Interface 對各類型的封裝上還有更進一步的簡化,下面使用 sort.Strings 繼續(xù)對代碼1進行簡化,代碼如下:

names := []string{
    "3. Triple Kill",
    "5. Penta Kill",
    "2. Double Kill",
    "4. Quadra Kill",
    "1. First Blood",
}

sort.Strings(names)

// 遍歷打印結(jié)果
for _, v := range names {
    fmt.Printf("%s\n", v)
}

代碼說明如下:

  • 第 1 行,需要排序的字符串切片。
  • 第 9 行,使用 sort.Strings 直接對字符串切片進行排序。

3) sort包內(nèi)建的類型排序接口一覽

Go語言中的 sort 包中定義了一些常見類型的排序方法,如下表所示。

sort 包中內(nèi)建的類型排序接口

類  型 實現(xiàn) sort.lnterface 的類型 直接排序方法 說  明
字符串(String) StringSlice sort.Strings(a [] string) 字符 ASCII 值升序
整型(int) IntSlice sort.Ints(a []int) 數(shù)值升序
雙精度浮點(float64) Float64Slice sort.Float64s(a []float64) 數(shù)值升序

編程中經(jīng)常用到的 int32、int64、float32、bool 類型并沒有由 sort 包實現(xiàn),使用時依然需要開發(fā)者自己編寫。

對結(jié)構(gòu)體數(shù)據(jù)進行排序

除了基本類型的排序,也可以對結(jié)構(gòu)體進行排序。結(jié)構(gòu)體比基本類型更為復雜,排序時不能像數(shù)值和字符串一樣擁有一些固定的單一原則。結(jié)構(gòu)體的多個字段在排序中可能會存在多種排序的規(guī)則,例如,結(jié)構(gòu)體中的名字按字母升序排列,數(shù)值按從小到大的順序排序。一般在多種規(guī)則同時存在時,需要確定規(guī)則的優(yōu)先度,如先按名字排序,再按年齡排序等。

1) 完整實現(xiàn)sort.Interface進行結(jié)構(gòu)體排序

將一批英雄名單使用結(jié)構(gòu)體定義,英雄名單的結(jié)構(gòu)體中定義了英雄的名字和分類。排序時要求按照英雄的分類進行排序,相同分類的情況下按名字進行排序,詳細代碼實現(xiàn)過程如下。

結(jié)構(gòu)體排序代碼(代碼2):

package main

import (
    "fmt"
    "sort"
)

// 聲明英雄的分類
type HeroKind int

// 定義HeroKind常量, 類似于枚舉
const (
    None HeroKind = iota
    Tank
    Assassin
    Mage
)

// 定義英雄名單的結(jié)構(gòu)
type Hero struct {
    Name string  // 英雄的名字
    Kind HeroKind  // 英雄的種類
}

// 將英雄指針的切片定義為Heros類型
type Heros []*Hero

// 實現(xiàn)sort.Interface接口取元素數(shù)量方法
func (s Heros) Len() int {
    return len(s)
}

// 實現(xiàn)sort.Interface接口比較元素方法
func (s Heros) Less(i, j int) bool {

    // 如果英雄的分類不一致時, 優(yōu)先對分類進行排序
    if s[i].Kind != s[j].Kind {
        return s[i].Kind < s[j].Kind
    }

    // 默認按英雄名字字符升序排列
    return s[i].Name < s[j].Name
}

// 實現(xiàn)sort.Interface接口交換元素方法
func (s Heros) Swap(i, j int) {
    s[i], s[j] = s[j], s[i]
}

func main() {

    // 準備英雄列表
    heros := Heros{
        &Hero{"呂布", Tank},
        &Hero{"李白", Assassin},
        &Hero{"妲己", Mage},
        &Hero{"貂蟬", Assassin},
        &Hero{"關(guān)羽", Tank},
        &Hero{"諸葛亮", Mage},
    }

    // 使用sort包進行排序
    sort.Sort(heros)

    // 遍歷英雄列表打印排序結(jié)果
    for _, v := range heros {
        fmt.Printf("%+v\n", v)
    }
}

代碼輸出如下:

&{Name:關(guān)羽 Kind:1}
&{Name:呂布 Kind:1}
&{Name:李白 Kind:2}
&{Name:貂蟬 Kind:2}
&{Name:妲己 Kind:3}
&{Name:諸葛亮 Kind:3}

代碼說明如下:

  • 第 9 行,將 int 聲明為 HeroKind 英雄類型,后面會將這個類型當做枚舉來使用。
  • 第 13 行,定義一些英雄類型常量,可以理解為枚舉的值。
  • 第 26 行,為了方便實現(xiàn) sort.Interface 接口,將 []*Hero 定義為 Heros 類型。
  • 第 29 行,Heros 類型實現(xiàn)了 sort.Interface 的 Len() 方法,返回英雄的數(shù)量。
  • 第 34 行,Heros 類型實現(xiàn)了 sort.Interface 的 Less() 方法,根據(jù)英雄字段的比較結(jié)果決定如何排序。
  • 第 37 行,當英雄的分類不一致時,優(yōu)先按分類的枚舉數(shù)值從小到大排序。
  • 第 42 行,英雄分類相等的情況下,默認根據(jù)英雄的名字字符升序排序。
  • 第 46 行,Heros 類型實現(xiàn)了 sort.Interface 的 Swap() 方法,交換英雄元素的位置。
  • 第 53~60 行,準備一系列英雄數(shù)據(jù)。
  • 第 63 行,使用 sort 包進行排序。
  • 第 66 行,遍歷所有排序完成的英雄數(shù)據(jù)。

2) 使用sort.Slice進行切片元素排序

從 Go 1.8 開始,Go語言在 sort 包中提供了 sort.Slice() 函數(shù)進行更為簡便的排序方法。sort.Slice() 函數(shù)只要求傳入需要排序的數(shù)據(jù),以及一個排序時對元素的回調(diào)函數(shù),類型為 func(i,j int)bool,sort.Slice() 函數(shù)的定義如下:

func Slice(slice interface{}, less func(i, j int) bool)

使用 sort.Slice() 函數(shù),對代碼2重新優(yōu)化的完整代碼如下:

package main

import (
    "fmt"
    "sort"
)

type HeroKind int

const (
    None = iota
    Tank
    Assassin
    Mage
)

type Hero struct {
    Name string
    Kind HeroKind
}

func main() {

    heros := []*Hero{
        {"呂布", Tank},
        {"李白", Assassin},
        {"妲己", Mage},
        {"貂蟬", Assassin},
        {"關(guān)羽", Tank},
        {"諸葛亮", Mage},
    }

    sort.Slice(heros, func(i, j int) bool {
        if heros[i].Kind != heros[j].Kind {
            return heros[i].Kind < heros[j].Kind
        }

        return heros[i].Name < heros[j].Name
    })

    for _, v := range heros {
        fmt.Printf("%+v\n", v)
    }
}

第 33 行到第 39 行加粗部分是新添加的 sort.Slice() 及回調(diào)函數(shù)部分。對比前面的代碼,這里去掉了 Heros 及接口實現(xiàn)部分的代碼。

使用 sort.Slice() 不僅可以完成結(jié)構(gòu)體切片排序,還可以對各種切片類型進行自定義排序。


當前文章:創(chuàng)新互聯(lián)GO教程:Go語言排序(借助sort.Interface接口)
URL網(wǎng)址:http://www.dlmjj.cn/article/dppgpos.html