golang 對自定義類型排序

golang 對自定義類型排序

在實際項目中,咱們經常須要根據一個結構體類型的某個字段進行排序。以前遇到這個問題不知道如何解決,後來在網上搜索了相關問題,找到了一些好的解決方案,此處參考下,作個總結吧。golang

因爲 golang 的 sort 包自己就提供了相應的功能, 咱們就不必重複的造個輪子了,來看看如何利用 sort 包來實現吧。算法

sort包淺談

sort 包 在內部實現了四種基本的排序算法:插入排序(insertionSort)、歸併排序(symMerge)、堆排序(heapSort)和快速排序(quickSort); sort 包會依據實際數據自動選擇最優的排序算法。因此咱們寫代碼時只須要考慮實現 sort.Interface 這個類型就能夠了。ui

粗略的看看sort包

func Sort(data Interface) {
    // Switch to heapsort if depth of 2*ceil(lg(n+1)) is reached.
    n := data.Len()
    maxDepth := 0
    for i := n; i > 0; i >>= 1 {
        maxDepth++
    }
    maxDepth *= 2
    quickSort(data, 0, n, maxDepth)
}

type Interface interface {
    // Len is the number of elements in the collection.
    Len() int
    // Less reports whether the element with
    // index i should sort before the element with index j.
    Less(i, j int) bool
    // Swap swaps the elements with indexes i and j.
    Swap(i, j int)
}
// 內部實現的四種排序算法
// 插入排序
func insertionSort(data Interface, a, b int)
// 堆排序
func heapSort(data Interface, a, b int)
// 快速排序
func quickSort(data Interface, a, b, maxDepth int)
// 歸併排序
func symMerge(data Interface, a, m, b int)

因此要調用 sort.Sort() 來實現自定義類型排序,只須要咱們的類型實現 Interface 接口類型中的三個方法便可。code

先看看 sort 包自己對於 []int 類型如何排序排序

// 首先定義了一個[]int類型的別名IntSlice 
type IntSlice []int
// 獲取此 slice 的長度
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.Ints()內部調用Sort() 方法實現排序
// 注意 要先將[]int 轉換爲 IntSlice類型 由於此類型才實現了Interface的三個方法 
func Ints(a []int) { Sort(IntSlice(a)) }

照葫蘆畫瓢 咱們來對自定義的結構體類型進行降序排序繼承

package main

import (
    "fmt"
    "sort"
)

type Person struct {
    Name string
    Age  int
}

type Persons []Person
// 獲取此 slice 的長度
func (p Persons) Len() int { return len(p) }
// 根據元素的年齡降序排序 (此處按照本身的業務邏輯寫) 
func (p Persons) Less(i, j int) bool {
    return p[i].Age > p[j].Age
}
// 交換數據
func (p Persons) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
func main() {
    persons := Persons{
        {
            Name: "test1",
            Age:  20,
        },
        {
            Name: "test2",
            Age:  22,
        },
        {
            Name: "test3",
            Age:  21,
        },
    }

    fmt.Println("排序前")
    for _, person := range persons {
        fmt.Println(person.Name, ":", person.Age)
    }
    sort.Sort(persons)
    fmt.Println("排序後")
    for _, person := range persons {
        fmt.Println(person.Name, ":", person.Age)
    }
}

其實,通常 Len() 和 Swap() 基本不作改變,只有涉及到元素比較的 Less() 方法會有所改變。
當咱們對某一個結構體中多個字段進行排序時怎麼辦,難道每排序一個就寫下這三個方法麼,固然不是。咱們能夠利用嵌套結構體來解決這個問題。由於嵌套結構體能夠繼承父結構體的全部屬性和方法
好比我想對上面 Person 的 Name 字段和 Age 對要排序,咱們能夠利用嵌套結構體來改進一下。接口

package main

import (
    "fmt"
    "sort"
)

type Person struct {
    Name string
    Age  int
}

type Persons []Person

// Len()方法和Swap()方法不用變化
// 獲取此 slice 的長度
func (p Persons) Len() int { return len(p) }

// 交換數據
func (p Persons) Swap(i, j int) { p[i], p[j] = p[j], p[i] }

// 嵌套結構體  將繼承 Person 的全部屬性和方法
// 因此至關於SortByName 也實現了 Len() 和 Swap() 方法
type SortByName struct{ Persons }

// 根據元素的姓名長度降序排序 (此處按照本身的業務邏輯寫)
func (p SortByName) Less(i, j int) bool {
    return len(p.Persons[i].Name) > len(p.Persons[j].Name)
}

type SortByAge struct{ Persons }

// 根據元素的年齡降序排序 (此處按照本身的業務邏輯寫)
func (p SortByAge) Less(i, j int) bool {
    return p.Persons[i].Age > p.Persons[j].Age
}

func main() {
    persons := Persons{
        {
            Name: "test123",
            Age:  20,
        },
        {
            Name: "test1",
            Age:  22,
        },
        {
            Name: "test12",
            Age:  21,
        },
    }

    fmt.Println("排序前")
    for _, person := range persons {
        fmt.Println(person.Name, ":", person.Age)
    }
    sort.Sort(SortByName{persons})
    fmt.Println("排序後")
    for _, person := range persons {
        fmt.Println(person.Name, ":", person.Age)
    }
}
相關文章
相關標籤/搜索