排序切片的任意类型

举报
码乐 发表于 2025/11/19 08:27:18 2025/11/19
【摘要】 1 简介Go sort 包的排序方法概述。Go 的排序主要由 sort 包提供,核心概念是 sort.Interface:type Interface interface { Len() int Less(i, j int) bool Swap(i, j int)}基于这个接口,Go 提供了三类排序方法: 2 面向基础类型的排序(已封装)Go 内置以下封装好的排序函数: ...

1 简介

Go sort 包的排序方法概述。

Go 的排序主要由 sort 包提供,核心概念是 sort.Interface:

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

基于这个接口,Go 提供了三类排序方法:

2 面向基础类型的排序(已封装)

Go 内置以下封装好的排序函数:

    sort.Ints([]int)

    sort.Float64s([]float64)

    sort.Strings([]string)

以及对应的返回是否有序的检查:

      sort.IntsAreSorted()

      sort.Float64sAreSorted()

      sort.StringsAreSorted()

int 排序

    ints := []int{3, 1, 2}
    sort.Ints(ints)

float 排序

  floats := []float64{1.2, 3.4, -2.0}
  sort.Float64s(floats)

string 排序(UTF-8 字符序)

Go 的 string 排序按「字节序/Unicode 编码点序」排序,即 UTF-8 lexicographical 排序。

对英文和 ASCII 字符效果符合直觉,但对中文是 按 Unicode 编码排序,不是按拼音或笔画排序。

    strs := []string{"中国", "美国", "日本"}
    sort.Strings(strs)

实际排序结果可能是:[“中国”, “日本”, “美国”](按 Unicode)

2 排序函数(更灵活) sort.Slice / sort.SliceStable

Go 1.8 之后推荐使用:

    sort.Slice(slice, func(i, j int) bool {
        return slice[i].Field < slice[j].Field
    })

稳定排序:

  sort.SliceStable(slice, func(i, j int) bool {
      return slice[i].Field < slice[j].Field
  })

适用于切片中存放自定义 struct 的场景。

  • 完整实现 sort.Interface 例子

实现以下三个方法即可:

    Len()
    Less(i,j)
    Swap(i,j)

适用:
需要高级排序规则(多字段排序、动态组合规则等)。

3 按拼音进行排序

最常用、最简洁的 Go 内置 sort 包 + 第三方拼音库 实现 “按中文拼音排序” 的完整示例。

这里使用社区中最常用的拼音库:

			github.com/mozillazg/go-pinyin

示例:使用 sort.Slice + pinyin 按中文拼音排序

    1. 安装第三方拼音包

         go get github.com/mozillazg/go-pinyin
      
    1. 示例代码

      import (
          "fmt"
          "sort"
      
          "github.com/mozillazg/go-pinyin"
      )
      
      func main() {
          names := []string{"张三", "李四", "王五", "赵六", "陈七", "欧阳修"}
      
          // 配置拼音风格
          a := pinyin.NewArgs()
          a.Style = pinyin.Normal // 普通风格,不带声调
      
          sort.Slice(names, func(i, j int) bool {
              // 转拼音(返回二维数组,每个汉字可能对应多个音)
              pi := pinyin.Pinyin(names[i], a)
              pj := pinyin.Pinyin(names[j], a)
      
              // 把二维数组转成可比较的拼音字符串
              si := flattenPinyin(pi)
              sj := flattenPinyin(pj)
      
              return si < sj
          })
      
          fmt.Println(names)
      }
      
      // 拼音二维数组转为字符串(欧阳 → ou yang)
      func flattenPinyin(py [][]string) string {
          res := ""
          for _, p := range py {
              if len(p) > 0 {
                  res += p[0]
              }
          }
          return res
      }
      
    1. 运行结果(示例)

排序前:

			[张三 李四 王五 赵六 陈七 欧阳修]

按拼音排序后:

		[陈七 李四 欧阳修 王五 张三 赵六]

拼音对照:

    名字	拼音
    陈七	chenqi
    李四	lisi
    欧阳修	ouyangxiu
    王五	wangwu
    张三	zhangsan
    赵六	zhaoliu

排序结果完全符合拼音顺序。

4 更常用 Struct 字段排序的版本

如果你有一个 struct,例如:

  type Person struct {
      Name string
      Age  int
  }

按 Name 的中文拼音排序:

  sort.Slice(people, func(i, j int) bool {
      pi := flattenPinyin(pinyin.Pinyin(people[i].Name, a))
      pj := flattenPinyin(pinyin.Pinyin(people[j].Name, a))
      return pi < pj
  })
  • 提升性能(避免每次都重新转拼音)

拼音转换成本较高,如果列表很大(几万条以上),最好 提前缓存拼音:

    type Person struct {
        Name      string
        PinyinKey string // 缓存拼音
    }

    func preprocessPinyin(p *Person) {
        a := pinyin.NewArgs()
        a.Style = pinyin.Normal
        p.PinyinKey = flattenPinyin(pinyin.Pinyin(p.Name, a))
    }

排序时只比较 PinyinKey:

    sort.Slice(people, func(i, j int) bool {
        return people[i].PinyinKey < people[j].PinyinKey
    })

效率可以提升数倍。

【版权声明】本文为华为云社区用户原创内容,未经允许不得转载,如需转载请自行联系原作者进行授权。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。