发布于2026-07-21 阅读(0)
扫一扫,手机访问
在 Go 语言里,排序这件事,其实核心就围绕着一个接口——sort.Interface。它就像是给排序算法和待排序数据之间定下的一份“契约”,让算法无需关心具体的数据类型,只要数据能遵守这个契约,就能被优雅地排序。
一个通用的排序算法,其实只需要知道三件事:序列有多长?怎么比较两个元素谁大谁小?怎么交换两个元素的位置?没错,这就是 sort.Interface 要求实现的三个方法:
package sort
type Interface interface {
Len() int
Less(i, j int) bool
Swap(i, j int)
}
换句话说,只要你的类型实现了这三个方法,就能直接调用 sort.Sort 来排序了。
举个最简单的例子:对一个字符串切片排序。我们可以定义一个全新的类型 StringSlice,然后给它配上这三个方法:
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]
}
注意 Less 方法里用的是 < 符号,这表示按升序排列。如果你想降序,改一下符号就行。
因为字符串切片排序实在太常见了,Go 标准库干脆直接帮你封装好了 sort.StringSlice 类型,你直接用就行:
s := []string{"a", "c", "b"}
sort.Sort(sort.StringSlice(s))
甚至还有更简洁的 sort.Strings 函数:
s := []string{"a", "c", "b"}
sort.Strings(s)
对于 []int、[]float64、[]string 这类基础类型的切片,直接用下面这几个函数就对了:
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 方法改得更精细一些:
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
}
这样排序结果就会变成:
[{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}]
}
这种方式的好处是:你可以把比较逻辑单独抽出来,甚至动态替换,非常灵活。不过日常开发中,直接用 sort.Slice 往往更省事。
总的来说,Go 的排序设计思路很清晰:定义一个接口,然后让数据去适配它。只要理解了 Len、Less、Swap 这三个方法,任何自定义类型都能轻松接入排序机制。
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
正版软件
正版软件
正版软件
正版软件
正版软件
1
2
3
7
8