sort.Interface
Go 语言的 sort.Sort 函数不会对具体的序列和它的元素做任何假设。它使用了一个接口类型 sort.Interface 来指定通用的排序算法和可能被排序到的序列类型之间的约定。
一个内置的排序算法需要知道三个东西:序列的长度,表示两个元素比较的结果,一种交换两个元素的方式; 这就是 sort.Interface 的三个方法:
package sort
type Interface interface {
Len() int
Less(i, j int) bool
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 int) bool { 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", 90, 30},
{"b", 100, 10},
{"c", 90, 20},
}
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", 90, 30},
{"b", 100, 10},
{"c", 90, 20},
}
sort.Sort(p)
fmt.Println(p) // [{b 100 10} {c 90 20} {c 90 30}]
}
可以使用 sort.Slice 进一步简化,该函数默认实现了排序所需要的 Len 和 Swap 方法,传入排序对象和 Less 方法即可:
p := Persons{
{"c", 90, 30},
{"b", 100, 10},
{"c", 90, 20},
}
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 int) bool { 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", 90, 30},
{"b", 100, 10},
{"c", 90, 20},
}
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}]
}