1. 结构体(切片)排序
结构体返回的是切片。
之前学习了sort.Ints()和sort.Strings(),使用这两个sort库下面的方法,可以对int和strings进行排序。
那如果我要对自定义类型进行排序,怎么办,sort库没提供,怎么办?可以参照sort源码自己写一个。
1.1 sort源码解析
一个需要排序的类型,必须要实现下面接口中的3个方法。
type Interface interface {Len() int // 能取长度,不管有多少元素,也不管是什么数据类型
Less(i, j int) bool // 可以取索引,比较大小
Swap(i, j int) // 交换i与j的索引位置
}对于切片来说,Len和Swap都还好处理,难点就在于Less的比较大小。
任意类型或者自定义的类型,如何使用Less进行大小比较呢?我也没有办法提前知道,这个切片中,到底会存放什么类型的元素。
1.1.1 sort.Ints源码解析
sort.Ints(),ctrl+鼠标左键点击ints,会跳转到sort.go,然后会有这样一行代码:
func Ints(x []int) { Sort(IntSlice(x)) },然后ctrl+鼠标左键点击IntSlice,会跳转到这个代码:
type IntSlice []int
func (x IntSlice) Len() int { return len(x) }
func (x IntSlice) Less(i, j int) bool { return x[i] < x[j] }
func (x IntSlice) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
调用sort.Ints()时,会调用func Ints(x []int) { Sort(IntSlice(x)) },也就是把我们的需要排序的元素,传到IntSlice(x),作用是把元素先转换为自定义IntSlice类型。
对于Len() int { return len(x) }来说,就是取出元素长度。
Swap(i, j int) { x[i], x[j] = x[j], x[i] },主要作用就是交换2个元素的位置。
Less(i, j int) bool { return x[i] < x[j] },比较元素大小,成立返回true,否则返回flase,并进行位置调整,也就是调用Swap方法,也就是默认升序的情况。
1.1.2 sort.Strings源码解析
sort.Strings()
func Strings(x []string) { Sort(StringSlice(x)) }
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] }
可以看到,int和strings的排序实现逻辑,其实是一样的。
1.2 自定义结构体(切片)排序
package main
import (
"fmt"
"sort"
)
type Runner interface {
run()
}
type Cat struct {
name string
age int
}
type CatSlice []Cat // 这里就相当于[]Cat{c1, c2, Cat{"Momo", 99}}
func (x CatSlice) Len() int { return len(x) }
func (x CatSlice) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
func (x CatSlice) Less(i, j int) bool { return x[i].age < x[j].age }
func main() {
var c1 = Cat{"Tom", 20}
var c2 = Cat{"Jerry", 18}
cats := []Cat{c1, c2, Cat{"Momo", 99}}
fmt.Println("排序前:", cats)
sort.Sort(CatSlice(cats))
fmt.Println("排序后:", cats)
}
=========调试结果=========
排序前: [{Tom 20} {Jerry 18} {Momo 99}]
排序后: [{Jerry 18} {Tom 20} {Momo 99}]
1.3 简化版结构体(切片)排序
sort.Slice(),是专门给切片用的。
package main
import (
"fmt"
"sort"
)
type Runner interface {
run()
}
type Cat struct {
name string
age int
}
func main() {
var c1 = Cat{"Tom", 20}
var c2 = Cat{"Jerry", 18}
cats := []Cat{c1, c2, Cat{"Momo", 99}}
fmt.Println("排序前:", cats)
sort.Slice(cats, func(i, j int) bool {
return cats[i].age < cats[j].age
})
fmt.Println("排序后:", cats)
}
=========调试结果=========
排序前: [{Tom 20} {Jerry 18} {Momo 99}]
排序后: [{Jerry 18} {Tom 20} {Momo 99}]
2. map排序
2.1 key排序
2.1.1 升序
package main
import (
"fmt"
"sort"
)
func main() {
m := make(map[int]string)
m[2] = "abc"
m[1] = "xvz"
m[100] = "aaa"
fmt.Println(m)
keys := make([]int, 0, len(m))
for k := range m {
keys = append(keys, k)
}
fmt.Println(keys)
sort.Ints(keys)
fmt.Println(keys)
for _, k := range keys {
fmt.Println(k, m[k])
}
}
=========调试结果=========
map[1:xvz 2:abc 100:aaa]
[2 1 100]
[1 2 100]
1 xvz
2 abc
100 aaa
2.1.2 降序
package main
import (
"fmt"
"sort"
)
func main() {
m := make(map[int]string)
m[2] = "abc"
m[1] = "xvz"
m[100] = "aaa"
fmt.Println(m)
keys := make([]int, 0, len(m))
for k := range m {
keys = append(keys, k)
}
fmt.Println(keys)
// sort.Ints(keys)
sort.Sort(sort.Reverse(sort.IntSlice(keys))) // 调整这里
fmt.Println(keys)
for _, k := range keys {
fmt.Println(k, m[k])
}
}
=========调试结果=========
map[1:xvz 2:abc 100:aaa]
[100 2 1]
[100 2 1]
100 aaa
2 abc
1 xvz
2.2 value排序
2.2.1 升序
package main
import (
"fmt"
"sort"
)
type Entry struct {
k int
v string
}
func main() {
m := make(map[int]string)
m[2] = "abc"
m[1] = "xvz"
m[100] = "aaa"
fmt.Println(m)
entries := make([]Entry, 0, len(m))
for k, v := range m {
entries = append(entries, Entry{k, v})
}
fmt.Println(entries)
sort.Slice(entries, func(i, j int) bool {
return entries[i].v < entries[j].v
})
fmt.Println(entries)
}
=========调试结果=========
map[1:xvz 2:abc 100:aaa]
[{1 xvz} {100 aaa} {2 abc}]
[{100 aaa} {2 abc} {1 xvz}]
3. 排序总结
排序,只支持线性数据结构,如果是hash类型,一定要先转换成线性数据结构。