6.2 Go 切片自定义排序学习笔记
1. sort.Slice — 自定义排序函数
sort.Slice(slice, lessFunc) 用自定义的比较函数对切片排序,lessFunc 返回 true 表示 i 应排在 j 前面:
package main
import (
"fmt"
"sort"
)
type Person struct {
Name string
Age int
Salary int
}
func printPeople(people []Person) {
for _, p := range people {
fmt.Printf(" %s (Age: %d, Salary: $%d)\n", p.Name, p.Age, p.Salary)
}
}
func main() {
people := []Person{
{"Alice", 28, 75000},
{"Bob", 32, 85000},
{"Charlie", 25, 65000},
{"Diana", 29, 90000},
{"Eve", 25, 70000},
}
fmt.Println("Original people:")
printPeople(people)
// 按年龄升序:people[i].Age < people[j].Age → i 排前面
sort.Slice(people, func(i, j int) bool {
return people[i].Age < people[j].Age
})
fmt.Println("\nSorted by age (ascending):")
printPeople(people)
// 按薪资降序:people[i].Salary > people[j].Salary → i 排前面(大在前=降序)
sort.Slice(people, func(i, j int) bool {
return people[i].Salary > people[j].Salary
})
fmt.Println("\nSorted by salary (descending):")
printPeople(people)
// 按名字字典序:people[i].Name < people[j].Name → i 排前面
sort.Slice(people, func(i, j int) bool {
return people[i].Name < people[j].Name
})
fmt.Println("\nSorted by name (alphabetical):")
printPeople(people)
}
执行结果:
Original people:
Alice (Age: 28, Salary: $75000)
Bob (Age: 32, Salary: $85000)
Charlie (Age: 25, Salary: $65000)
Diana (Age: 29, Salary: $90000)
Eve (Age: 25, Salary: $70000)
Sorted by age (ascending):
Charlie (Age: 25, Salary: $65000)
Eve (Age: 25, Salary: $70000)
Alice (Age: 28, Salary: $75000)
Diana (Age: 29, Salary: $90000)
Bob (Age: 32, Salary: $85000)
Sorted by salary (descending):
Diana (Age: 29, Salary: $90000)
Bob (Age: 32, Salary: $85000)
Alice (Age: 28, Salary: $75000)
Eve (Age: 25, Salary: $70000)
Charlie (Age: 25, Salary: $65000)
Sorted by name (alphabetical):
Alice (Age: 28, Salary: $75000)
Bob (Age: 32, Salary: $85000)
Charlie (Age: 25, Salary: $65000)
Diana (Age: 29, Salary: $90000)
Eve (Age: 25, Salary: $70000)
要点:
-
sort.Slice(slice, func(i, j int) bool { ... })— 两个参数:切片 + 比较函数 - 比较函数接收两个索引
i, j,返回true表示slice[i]应排在slice[j]前面 - 升序:
<(小的排前面)—people[i].Age < people[j].Age - 降序:
>(大的排前面)—people[i].Salary > people[j].Salary - 字典序:字符串的
<比较按字母顺序 —people[i].Name < people[j].Name -
sort.Slice是不稳定排序:相等元素的相对顺序可能改变(见知识点 2)
2. sort.SliceStable — 稳定排序(保持相等元素的原始顺序)
sort.SliceStable 在排序时保持相等元素的原始相对顺序:
package main
import (
"fmt"
"sort"
)
type Person struct {
Name string
Age int
Salary int
}
func printPeople(people []Person) {
for _, p := range people {
fmt.Printf(" %s (Age: %d, Salary: $%d)\n", p.Name, p.Age, p.Salary)
}
}
func main() {
// 先按名字排序,建立初始顺序
people := []Person{
{"Alice", 28, 75000},
{"Bob", 32, 85000},
{"Charlie", 25, 65000},
{"Diana", 29, 90000},
{"Eve", 25, 70000},
}
// 多级排序:先按年龄,年龄相同则按薪资降序
sort.SliceStable(people, func(i, j int) bool {
if people[i].Age == people[j].Age {
return people[i].Salary > people[j].Salary // 同年龄,薪资高排前面
}
return people[i].Age < people[j].Age // 不同年龄,年龄小排前面
})
fmt.Println("Sorted by age, then salary (stable sort):")
printPeople(people)
}
执行结果:
Sorted by age, then salary (stable sort):
Eve (Age: 25, Salary: $70000)
Charlie (Age: 25, Salary: $65000)
Alice (Age: 28, Salary: $75000)
Diana (Age: 29, Salary: $90000)
Bob (Age: 32, Salary: $85000)
要点:
-
sort.SliceStable与sort.Slice语法相同,但保证相等元素的原始相对顺序不变 - 多级排序模式:
if 纀级1相等 { return 瀑级2比较 } else { return 瀑级1比较 } - 本例:Charlie(25,
70000) 年龄相同,按薪资降序 → Eve 排前面
- 对比:如果用
sort.Slice(不稳定),Charlie 和 Eve 的相对顺序可能颠倒 - 何时用 SliceStable:需要保持相等元素的相对顺序时(如先按A排序再按B排序的场景)
3. 复杂自定义排序 — 分类优先 + 二级排序
比较函数可以包含任意复杂的逻辑:
package main
import (
"fmt"
"sort"
)
type Person struct {
Name string
Age int
Salary int
}
func printPeople(people []Person) {
for _, p := range people {
fmt.Printf(" %s (Age: %d, Salary: $%d)\n", p.Name, p.Age, p.Salary)
}
}
func main() {
people := []Person{
{"Alice", 28, 75000},
{"Bob", 32, 85000},
{"Charlie", 25, 65000},
{"Diana", 29, 90000},
{"Eve", 25, 70000},
}
// 自定义排序:30岁以下优先,然后按薪资降序
sort.Slice(people, func(i, j int) bool {
under30i := people[i].Age < 30
under30j := people[j].Age < 30
if under30i && !under30j {
return true // i <30, j >=30 → i 排前面
}
if !under30i && under30j {
return false // i >=30, j <30 → j 排前面
}
// 同类别(都<30或都>=30),按薪资降序
return people[i].Salary > people[j].Salary
})
fmt.Println("Custom sort (under 30s first, then by salary desc):")
printPeople(people)
}
执行结果:
Custom sort (under 30s first, then by salary desc):
Diana (Age: 29, Salary: $90000)
Alice (Age: 28, Salary: $75000)
Eve (Age: 25, Salary: $70000)
Charlie (Age: 25, Salary: $65000)
Bob (Age: 32, Salary: $85000)
要点:
- 分类优先:先判断是否<30(布尔分组),<30的人排在前面
- 同组内再排序:同为<30的人按薪资降序,同为>=30的人也按薪资降序
- 4个30岁以下的人(Diana, Alice, Eve, Charlie)排在前面,按薪资降序
- Bob(32岁,>=30)排在最后,即使薪资
65000
- 这是"分类排序"模式:先分大类,大类内再细排,常见于分组展示场景
4. 字符串多级排序 — 先按长度再按字典序
package main
import (
"fmt"
"sort"
"strings"
)
func main() {
words := []string{"apple", "pie", "banana", "cat", "elephant", "dog", "a"}
fmt.Printf("Original words: %v\n", words)
// 先按长度排序,长度相同则按字典序
sort.Slice(words, func(i, j int) bool {
if len(words[i]) == len(words[j]) {
return strings.ToLower(words[i]) < strings.ToLower(words[j])
}
return len(words[i]) < len(words[j])
})
fmt.Printf("Sorted by length, then alphabetically: %v\n", words)
}
执行结果:
Original words: [apple pie banana cat elephant dog a]
Sorted by length, then alphabetically: [a cat dog pie apple banana elephant]
要点:
- 多级排序模式:
if 瀑级1相等 { return 瀑级2比较 } else { return 瀑级1比较 } - 先按长度:
len(words[i]) < len(words[j])— 短的排前面 - 长度相同按字典序:
strings.ToLower(words[i]) < strings.ToLower(words[j])— 大小写不敏感 - 结果:a(1) → cat/dog(3,字典序) → pie(3) → apple(5) → banana(6) → elephant(8)
- 注意 cat 和 dog 长度都是3,按字典序 cat < dog
5. 按绝对值排序 — 自定义数值排序
package main
import (
"fmt"
"sort"
)
func abs(x int) int {
if x < 0 {
return -x
}
return x
}
func main() {
numbers := []int{-5, 3, -1, 8, -10, 2, -3}
fmt.Printf("Original numbers: %v\n", numbers)
sort.Slice(numbers, func(i, j int) bool {
return abs(numbers[i]) < abs(numbers[j])
})
fmt.Printf("Sorted by absolute value: %v\n", numbers)
}
执行结果:
Original numbers: [-5 3 -1 8 -10 2 -3]
Sorted by absolute value: [-1 2 3 -3 -5 8 -10]
要点:
-
abs()辅助函数计算绝对值,在比较函数中调用 -
abs(numbers[i]) < abs(numbers[j])— 按绝对值升序排 - 结果:|-1|=1 → |2|=2 → |3|=3 → |-3|=3 → |-5|=5 → |8|=8 → |-10|=10
- 3 和 -3 绝对值都是3,相对顺序可能不同(sort.Slice 不稳定)
- 自定义排序不限于简单比较,可以在 less 函数中做任意计算
6. sort.SliceIsSorted — 检查是否已按自定义规则排序
package main
import (
"fmt"
"sort"
)
func main() {
ages := []int{25, 30, 35, 40, 45}
isSorted := sort.SliceIsSorted(ages, func(i, j int) bool {
return ages[i] <= ages[j] // 升序规则(注意用 <= 而不是 <)
})
fmt.Printf("Ages %v is sorted: %t\n", ages, isSorted)
}
执行结果:
Ages [25 30 35 40 45] is sorted: true
要点:
-
sort.SliceIsSorted(slice, lessFunc)— 检查切片是否已按自定义规则排序 - 比较函数与
sort.Slice使用相同的逻辑:ages[i] <= ages[j]表示升序 - 注意用
<=而不是<:<=允许相等元素,<会认为相等元素是"未排序" -
%t— 格式化布尔值为 "true" 或 "false" - 返回 true 表示切片已按该规则排序,false 表示未排序
知识点总结
| 知识点 | 关键概念 |
|---|---|
| sort.Slice |
sort.Slice(slice, func(i,j) bool) — 自定义比较函数排序 |
| 升序/降序 | 升序 <,降序 >,字典序用字符串 <
|
| sort.SliceStable | 稳定排序,相等元素保持原始相对顺序 |
| 多级排序 | if 瀑级1相等 { 瀑级2比较 } else { 瀑级1比较 } |
| 分类排序 | 先分组(布尔条件),组内再排序 |
| 自定义计算排序 | less 函数中可调用辅助函数(如 abs) |
| SliceIsSorted |
sort.SliceIsSorted(slice, lessFunc) 检查是否已排序,用 <=
|