Go sort.Interface

0 阅读3分钟
sort.Interface

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

一个内置的排序算法需要知道三个东西:序列的长度,表示两个元素比较的结果,一种交换两个元素的方式; 这就是 sort.Interface 的三个方法:

package sort

type Interface interface {
 Len() int
 Less(i, j intbool
 Swap(i, j int)
}

为了对序列进行排序,我们需要定义一个实现了这三个方法的类型,然后对这个类型的一个实例应用 sort.Sort 函数。

思考对一个字符串切片进行排序,这可能是最简单的例子了。下面是实现了一个新的类型 StringSlice 和它的 Len,Less 和 Swap 方法:

type StringSlice []string

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

func main() {
 s := []string{"a""c""b"}
 sort.Sort(StringSlice(s))
 fmt.Println(s) // [a b c]
}

现在我们可以通过像下面这样将一个切片转换为一个 StringSlice类型来进行排序:

 sort.Sort(StringSlice(s))

上述 Less 方法中 " < " 代表按升序排序。

基础类型的切片排序

对字符串切片的排序是很常用的需要,所以 sort 包直接提供了 StringSlice 类型:

 s := []string{"a""c""b"}
 sort.Sort(sort.StringSlice(s))

也提供了 Strings 函数能让上面这些调用简化成:

 s := []string{"a""c""b"}
 sort.Strings(s)

总结一下,对于 []int, []float64, []string 这种元素类型是基础类型的切片,使用 sort 包提供的下面几个函数进行排序。

sort.Ints
sort.Floats
sort.Strings
结构体元素类型切片排序

现在有一个元素类型为 Person 结构体的切片,结构体对象有 Name,Score,Age 字段,现按照 Name 进行排序:

type Person struct {
 Name  string
 Score int
 Age   int
}

type Persons []Person

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

func main() {
 p := Persons{
  {"c", 9030},
  {"b", 10010},
  {"c", 9020},
 }
 sort.Sort(p)
 fmt.Println(p) // [{b 100 10} {c 90 30} {c 90 20}]
}

新增需求如果 Name 相同,按 Score 排序,Score 相同,按 Age 排序,修改 Less 方法:

type Person struct {
 Name  string
 Score int
 Age   int
}

type Persons []Person

func (p Persons) Len() int { return len(p) }
func (p Persons) Less(i, j int) bool {
 if p[i].Name != p[j].Name {
  return p[i].Name < p[j].Name
 }
 if p[i].Score != p[j].Score {
  return p[i].Score < p[j].Score
 }
 return p[i].Age < p[j].Age
}
func (p Persons) Swap(i, j int) { p[i]p[j] = p[j]p[i] }

func main() {
 p := Persons{
  {"c", 9030},
  {"b", 10010},
  {"c", 9020},
 }
 sort.Sort(p)
 fmt.Println(p) // [{b 100 10} {c 90 20} {c 90 30}]
}

可以使用 sort.Slice 进一步简化,该函数默认实现了排序所需要的 Len 和 Swap 方法,传入排序对象和 Less 方法即可:

 p := Persons{
  {"c", 9030},
  {"b", 10010},
  {"c", 9020},
 }

 sort.Slice(p, func(i, j int) bool {
  if p[i].Name != p[j].Name {
   return p[i].Name < p[j].Name
  }
  if p[i].Score != p[j].Score {
   return p[i].Score < p[j].Score
  }
  return p[i].Age < p[j].Age
 })

 fmt.Println(p) // [{b 100 10} {c 90 20} {c 90 30}]
任意数据结构排序

实现了 sort.Interface 的具体类型不一定是切片类型,下面为PersonSort 结构体类型实现排序接口:

type Person struct {
 Name  string
 Score int
 Age   int
}

type PersonSort struct {
 p    []Person
 less func(i, j Person) bool
}

func (p PersonSort) Len() int { return len(p.p) }

func (p PersonSort) Less(i, j intbool { return p.less(p.p[i], p.p[j]) }

func (p PersonSort) Swap(i, j int) { p.p[i], p.p[j] = p.p[j], p.p[i] }

func main() {
 p := []Person{
  {"c"9030},
  {"b"10010},
  {"c"9020},
 }

 less := func(i, j Person) bool {
  if i.Name != j.Name {
   return i.Name < j.Name
  }
  if i.Score != j.Score {
   return i.Score < j.Score
  }
  return i.Age < j.Age
 }

 sort.Sort(PersonSort{p, less})
 fmt.Println(p) // [{b 100 10} {c 90 20} {c 90 30}]
}