1.26 Go语言之排序(sort.Interface接口)

1.26 Go语言之排序(sort.Interface接口)

Go语言的sort函数

特点:

  • Go语言的sort.Sort函数不会对具体的序列和它的元素做任何假设用了一个接口类型sort.Interface来指定通用的排序算法和可能被排序到的序列类型之间的约定。

  • 接口的实现由序列的具体表示和它希望排序的元素决定,序列的表示经常是一个切片

sort.Interface定义的三个方法:

package sort
type Interface interface {
   Len() int            // 获取元素数量
   Less(i, j int) bool // i,j是序列元素的指数。
   Swap(i, j int)        // 交换元素
}

使用要点:

需要定义一个实现了这三个方法的类型,然后对这个类型的一个实例应用sort.Sort函数

使用sort.Interface接口进行排序

定义一个类型,实现sort.Interface接口当中的方法:

/*
将[]string定义为类型
Go的接口是隐式的,所以定义类型需要实现接口当中的方法
接口实现不受限于结构体,任何类型都可以实现接口。要排序的字符串切片 []string 是系统定制好的类型,无法让这个类型去实现 sort.Interface 排序接口。因此,需要将 []string 定义为自定义的类型
*/
type MyStringList []string

// 实现sort.Interface接口获取元素数量的方法
func (m MyStringList) Len() int {
   return len(m)
}

// 实现sort.Interface比较元素的方法
func (m MyStringList) Less(i, j int) bool {
   return m[i] < m[j]
}

// 实现sort.Interface交换元素的方法
func (m MyStringList) Swap(i, j int) {
   m[i], m[j] = m[j], m[i]
}

调用sort.Sort方法进行排序:

func main() {
   // 准备一个内容被打乱的字符串切片
   /*
   由于将 []string 定义成 MyStringList 类型,字符串切片初始化
    */
   names := MyStringList{
       "3, 这是三",
       "4, 这是四",
       "5, 这是五",
       "1, 这是一",
       "2, 这是二",
  }

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

   // 遍历打印结果
   for _, v := range names {
       fmt.Sprintf("%s\n", v)
  }
}

names := MyStringList{}写法等价于:

names := []string {
   "3, 这是三",
   "4, 这是四",
   "5, 这是五",
   "1, 这是一",
   "2, 这是二",
}

sort.Sort包内常见的类型排序接口

类 型实现 sort.lnterface 的类型直接排序方法说 明
字符串(String) StringSlice sort.Strings(a [] string) 字符 ASCII 值升序
整型(int) IntSlice sort.Ints(a []int) 数值升序
双精度浮点(float64) Float64Slice sort.Float64s(a []float64) 数值升序

经常用到的 int32、int64、float32、bool 类型并没有由 sort 包实现,使用时需要自己去实现sort.Interface接口实现。

对结构体数据进行排序

注意:

结构体的不同属性有不同的排序规则

代码示例

定义一批英雄名单使用结构体,结构体中定义了英雄的名字和分类。排序时要求按照英雄的分类进行排序,相同分类的情况下按名字进行排序

package main

import (
   "fmt"
   "sort"
)

/*
声明分类
将 int 声明为 HeroKind 英雄类型,后面会将这个类型当做枚举来使用
*/
type HeroKind int

/*
定义类型
*/
type Hero struct {
   Name string
   Kind HeroKind
}

/*
定义指针切片类型
*/
type Heros []*Hero

/*
定义常量--->类似枚举类
*/
const (
   None HeroKind = iota
   Tank
   Assassin
   Mage
)

/*
实现sort.Interface的方法
*/
// 取接口元素数量
func (s Heros) Len() int {
   return len(s)
}

// 比较接口元素
func (s Heros) Less(i, j int) bool {
   // 先判断分类是否一致,分类不一致优先对分类进行排序
   if s[i].Kind != s[j].Kind {
       // 对分类进行排序
       return s[i].Kind < s[j].Kind
  }

   // 默认按照名字升序排序
   return s[i].Name < s[j].Name
}

// 交换元素位置
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{"关羽", Tank},
       &Hero{"诸葛亮", Mage},
  }

   // 调用sort包排序
   sort.Sort(heros)

   // 遍历列表打印结果
   for _, v := range heros {
       fmt.Printf("%v\n", v)
  }
}

使用sort.Slice进行切片排序

特点:

我们知道Go当中是隐式的实现接口。但是依然需要实现接口当中的全部的方法。否则就会报错。

sort.Slice提供了更为简便的排序方法。入参只有:数据+排序时对元素的回调函数即可:

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

代码示例

package main

import (
   "fmt"
   "sort"
)

/*
定义分类
*/
type herokind int

/*
定义枚举常数
*/
const (
   none herokind = iota
   tank
   assassin
   mage
)

/*
定义类型
*/
type hero struct {
   name string
   kind herokind
}

/*
调用
*/
func main() {
   heros := []*hero{
      {"吕布", tank},
      {"李白", assassin},
      {"妲己", mage},
      {"貂蝉", assassin},
      {"关羽", tank},
      {"诸葛亮", mage},
  }

   // 调用sort.Slice函数
   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)
  }
}

特点:

传入回调函数就可以直观的看出本次操作执行了的函数是什么。而不是使用类型实现sort.Interface接口下全部的方法

使用sort.Slice()不仅可以完成结构体切片排序,还可以对各种切片类型进行自定义排序。

posted @ 2022-02-28 09:35  俊king  阅读(398)  评论(0)    收藏  举报