Go语言排序黑魔法大公开:自定义排序新姿势!

简介: Go语言排序黑魔法大公开:自定义排序新姿势!

/ Go 语言排序 /


 

一、概述

排序是编程中常见的算法问题,Go 语言标准库提供了 sort 包实现排序功能。sort 包定义了 Interface 接口,可以针对自定义数据类型实现排序,十分灵活。

本文将详细介绍如何使用 sort.Interface 接口进行 Go 语言排序,内容包括:

  • sort.Interface 接口说明
  • 基本类型排序
  • 数组切片排序
  • 结构体排序
  • 字典排序
  • 联合排序
  • 稳定性
  • 优化方法
  • 正反序

通过学习本文提供的示例,将能够熟练使用 sort.Interface 接口来对 Go 语言中的各种数据类型进行排序操作。


 

二、sort.Interface 接口

sort 包定义了 Interface 接口用于排序:

type Interface interface {
// Len() int:用于返回切片的长度。
Len() int  
// 用于比较切片中索引 i 和索引 j 处的元素,
// 如果 i 处的元素应该排在 j 处的元素之前,
// 则返回 true,否则返回 false。
Less(i, j int) bool 
// 用于交换切片中索引 i 和索引 j 处的元素。
Swap(i, j int)
}

要对一个类型进行排序,需要实现上述 3 个方法:

  • Len() 返回序列长度
  • Less() 比较两个元素顺序
  • Swap() 交换两个元素

然后可以调用 sort.Sort()传入实现了 Interface 的类型即可排序。


 

三、基本类型排序

基本类型 Slice 可以直接排序:

package main
import (
  "fmt"
  "sort"
)
func main() {
  nums := []int{5, 2, 6, 3, 1, 4}
  // 使用 sort.Ints 对整数切片进行升序排序
  sort.Ints(nums)
  // 输出排序后的结果
  fmt.Println(nums)
}

sort 包为常用基本类型提供了默认排序函数。


 

四、数组切片排序

对于数组切片,需要传递一个实现 Interface 的类型:

package main
import (
  "fmt"
  "sort"
)
type IntArray []int
func (p IntArray) Len() int {
  return len(p)
}
func (p IntArray) Less(i, j int) bool {
  return p[i] < p[j]
}
func (p IntArray) Swap(i, j int) {
  p[i], p[j] = p[j], p[i]
}
func main() {
  array := IntArray{5, 3, 7, 1}
  // 使用 sort.Sort 对 IntArray 进行排序
  sort.Sort(sort.Interface(array))
  // 输出排序后的结果
  fmt.Println(array)
}

实现 Len、Less、Swap 后即可排序。


 

五、结构体排序

对结构体排序,也需要定义一个 struct 类型实现 Interface 接口:

package main
import (
  "fmt"
  "sort"
)
type User struct {
  Name string
  Age  int
}
type UserArray []User
func (p UserArray) Len() int {
  return len(p)
}
func (p UserArray) Less(i, j int) bool {
  return p[i].Age < p[j].Age
}
func (p UserArray) Swap(i, j int) {
  p[i], p[j] = p[j], p[i]
}
func main() {
  users := UserArray{
    {"Bob", 23},
    {"Alice", 18},
  }
  // 使用 sort.Sort 对 UserArray 进行排序
  sort.Sort(users)
  // 输出排序后的结果
  for _, user := range users {
    fmt.Printf("Name: %s, Age: %d\n", user.Name, user.Age)
  }
}

这样可以按结构体指定字段排序。


 

六、字典排序

对 map 进行排序时需要先获取 key 或 value,然后进行排序:

package main
import (
  "fmt"
  "sort"
)
func main() {
  map1 := map[string]int{
    "a": 20,
    "b": 10,
    "c": 15,
  }
  // 按key排序
  keys := make([]string, 0, len(map1))
  for k := range map1 {
    keys = append(keys, k)
  }
  sort.Strings(keys)
  fmt.Println("按key排序:")
  for _, key := range keys {
    fmt.Printf("%s: %d\n", key, map1[key])
  }
  // 按value排序
  values := make([]int, 0, len(map1))
  for _, v := range map1 {
    values = append(values, v)
  }
  sort.Ints(values)
  fmt.Println("按value排序:")
  for _, value := range values {
    for key, v := range map1 {
      if v == value {
        fmt.Printf("%s: %d\n", key, value)
        break
      }
    }
  }
}

先提取键值到 slice,然后分别排序。


 

七、联合排序

可以自定义结构体进行联合排序:

package main
import (
  "fmt"
  "sort"
)
type User struct {
  Name string
  Age  int
}
type MultiSort struct {
  users []User
  ages  []int
}
func (m MultiSort) Len() int {
  return len(m.users)
}
func (m MultiSort) Less(i, j int) bool {
  if m.ages[i] < m.ages[j] {
    return true
  }
  if m.ages[i] > m.ages[j] {
    return false
  }
  return m.users[i].Name < m.users[j].Name
}
func (m MultiSort) Swap(i, j int) {
  m.users[i], m.users[j] = m.users[j], m.users[i]
  m.ages[i], m.ages[j] = m.ages[j], m.ages[i]
}
func main() {
  users := []User{
    {"Alice", 39},
    {"Bob", 23},
    {"Chris", 27},
  }
  ages := []int{39, 23, 27}
  multiSort := MultiSort{users, ages}
  // 使用 sort.Sort 进行联合排序
  sort.Sort(multiSort)
  // 输出排序后的结果
  fmt.Println(multiSort.users)
}

这样可以先按 ages 排序,年龄相同时再按 name 排序。


 

八、稳定性

排序算法分为稳定排序和不稳定排序。

简单来说,如果相等元素原始顺序保持不变就是稳定排序。

Go 语言排序算法采用的是快速排序,属于不稳定排序。如果需要稳定排序,需自己实现。

稳定排序示例:

package main
import (
  "fmt"
  "sort"
)
type User struct {
  Name string
  Age  int
}
type Users []User
func (u Users) Len() int           { return len(u) }
func (u Users) Swap(i, j int)      { u[i], u[j] = u[j], u[i] }
func (u Users) Less(i, j int) bool { return u[i].Age < u[j].Age }
func main() {
  users := Users{
    {"Alice", 37},
    {"Bob", 25},
    {"Chris", 33},
  }
  // 自定义稳定排序
  sort.Stable(users)
  fmt.Println(users)
}

九、优化方法

排序性能关键在于 Less()和 Swap()方法。

一些优化思路:

  • Less()方法实现要高效
  • Swap()若不需交换可以直接返回
  • 少量元素使用插入排序
  • 多线程并行排序

可以通过组合排序方式优化不同场景下的性能。

优化 Less 和 Swap:

package main
import (
  "fmt"
  "sort"
)
func main() {
  data := []int{5, 3, 6, 2, 1}
  // 优化 Less 函数
  sort.Slice(data, func(i, j int) bool {
    return data[i] < data[j]
  })
  fmt.Println("Sorted Data (optimized Less):", data)
  // 优化 Swap 函数
  sort.Slice(data, func(i, j int) bool {
    data[i], data[j] = data[j], data[i]
    return data[i] < data[j]
  })
  fmt.Println("Reversed Data (optimized Swap):", data)
}

十、正反序

sort 包也提供了 Reverse()反转排序:

package main
import (
  "fmt"
  "sort"
)
func main() {
  nums := []int{5, 2, 4, 3, 1}
  // 正序排序
  sort.Ints(nums)
  fmt.Println("升序排序结果:", nums)
  // 反序排序
  sort.Sort(sort.Reverse(sort.IntSlice(nums)))
  fmt.Println("反序排序结果:", nums)
}

这样可以实现排序结果的反转。


 

十一、总结

sort.Interface 接口提供了非常灵活的排序功能。学习使用该接口可以自由控制各种数据类型的排序逻辑。

本文从基本类型、数组切片、结构体等不同例子介绍了 sort.Interface 的用法,并讨论了稳定性、优化手段等问题,最后给出了一些高级应用技巧。希望可以帮助你熟练掌握 Go 语言中的排序方法。


目录
相关文章
|
2天前
|
安全 Java Go
探索Go语言在高并发环境中的优势
在当今的技术环境中,高并发处理能力成为评估编程语言性能的关键因素之一。Go语言(Golang),作为Google开发的一种编程语言,以其独特的并发处理模型和高效的性能赢得了广泛关注。本文将深入探讨Go语言在高并发环境中的优势,尤其是其goroutine和channel机制如何简化并发编程,提升系统的响应速度和稳定性。通过具体的案例分析和性能对比,本文揭示了Go语言在实际应用中的高效性,并为开发者在选择合适技术栈时提供参考。
|
6天前
|
运维 Kubernetes Go
"解锁K8s二开新姿势!client-go:你不可不知的Go语言神器,让Kubernetes集群管理如虎添翼,秒变运维大神!"
【8月更文挑战第14天】随着云原生技术的发展,Kubernetes (K8s) 成为容器编排的首选。client-go作为K8s的官方Go语言客户端库,通过封装RESTful API,使开发者能便捷地管理集群资源,如Pods和服务。本文介绍client-go基本概念、使用方法及自定义操作。涵盖ClientSet、DynamicClient等客户端实现,以及lister、informer等组件,通过示例展示如何列出集群中的所有Pods。client-go的强大功能助力高效开发和运维。
27 1
|
6天前
|
算法 NoSQL 中间件
go语言后端开发学习(六) ——基于雪花算法生成用户ID
本文介绍了分布式ID生成中的Snowflake(雪花)算法。为解决用户ID安全性与唯一性问题,Snowflake算法生成的ID具备全局唯一性、递增性、高可用性和高性能性等特点。64位ID由符号位(固定为0)、41位时间戳、10位标识位(含数据中心与机器ID)及12位序列号组成。面对ID重复风险,可通过预分配、动态或统一分配标识位解决。Go语言实现示例展示了如何使用第三方包`sonyflake`生成ID,确保不同节点产生的ID始终唯一。
go语言后端开发学习(六) ——基于雪花算法生成用户ID
|
1天前
|
监控 NoSQL Go
Go语言中高效使用Redis的Pipeline
Redis 是构建高性能应用时常用的内存数据库,通过其 Pipeline 和 Watch 机制可批量执行命令并确保数据安全性。Pipeline 类似于超市购物一次性结账,减少网络交互时间,提升效率。Go 语言示例展示了如何使用 Pipeline 和 Pipelined 方法简化代码,并通过 TxPipeline 保证操作原子性。Watch 机制则通过监控键变化实现乐观锁,防止并发问题导致的数据不一致。这些机制简化了开发流程,提高了应用程序的性能和可靠性。
5 0
|
4天前
|
NoSQL Go Redis
Go语言中如何扫描Redis中大量的key
在Redis中,遍历大量键时直接使用`KEYS`命令会导致性能瓶颈,因为它会一次性返回所有匹配的键,可能阻塞Redis并影响服务稳定性。为解决此问题,Redis提供了`SCAN`命令来分批迭代键,避免一次性加载过多数据。本文通过两个Go语言示例演示如何使用`SCAN`命令:第一个示例展示了基本的手动迭代方式;第二个示例则利用`Iterator`简化迭代过程。这两种方法均有效地避免了`KEYS`命令的性能问题,并提高了遍历Redis键的效率。
13 0
|
5天前
|
监控 Serverless Go
Golang 开发函数计算问题之Go 语言中切片扩容时需要拷贝原数组中的数据如何解决
Golang 开发函数计算问题之Go 语言中切片扩容时需要拷贝原数组中的数据如何解决
|
5天前
|
关系型数据库 MySQL 数据库连接
Go语言中使用sqlx来操作事务
在应用中,数据库事务保证操作的ACID特性至关重要。`github.com/jmoiron/sqlx`简化了数据库操作。首先安装SQLX和MySQL驱动:`go get github.com/jmoiron/sqlx`和`go get github.com/go-sql-driver/mysql`。导入所需的包后,创建数据库连接并使用`Beginx()`方法开始事务。通过`tx.Commit()`提交或`tx.Rollback()`回滚事务以确保数据一致性和完整性。
8 0
|
算法 Go 搜索推荐
冒泡排序和选择排序(Go语言实现)
冒泡排序和选择排序是排序算法中比较简单和容易实现的算法。冒泡排序的思想为:每一次排序过程,通过相邻元素的交换,将当前没有排好序中的最大(小)移到数组的最右(左)端。而选择排序的思想也很直观:每一次排序过程,我们获取当前没有排好序中的最大(小)的元素和数组最右(左)端的元素交换,循环这个过程即可实现对整个数组排序。
1114 0
|
9天前
|
JSON 中间件 Go
go语言后端开发学习(四) —— 在go项目中使用Zap日志库
本文详细介绍了如何在Go项目中集成并配置Zap日志库。首先通过`go get -u go.uber.org/zap`命令安装Zap,接着展示了`Logger`与`Sugared Logger`两种日志记录器的基本用法。随后深入探讨了Zap的高级配置,包括如何将日志输出至文件、调整时间格式、记录调用者信息以及日志分割等。最后,文章演示了如何在gin框架中集成Zap,通过自定义中间件实现了日志记录和异常恢复功能。通过这些步骤,读者可以掌握Zap在实际项目中的应用与定制方法
go语言后端开发学习(四) —— 在go项目中使用Zap日志库
|
6天前
|
SQL 关系型数据库 MySQL
Go语言中使用 sqlx 来操作 MySQL
Go语言因其高效的性能和简洁的语法而受到开发者们的欢迎。在开发过程中,数据库操作不可或缺。虽然Go的标准库提供了`database/sql`包支持数据库操作,但使用起来稍显复杂。为此,`sqlx`应运而生,作为`database/sql`的扩展库,它简化了许多常见的数据库任务。本文介绍如何使用`sqlx`包操作MySQL数据库,包括安装所需的包、连接数据库、创建表、插入/查询/更新/删除数据等操作,并展示了如何利用命名参数来进一步简化代码。通过`sqlx`,开发者可以更加高效且简洁地完成数据库交互任务。
13 1