Go语言排序(借助sort.Interface接口)
来源:云海天教程
时间:2022-12-28 13:12:51 303浏览 收藏
怎么入门Golang编程?需要学习哪些知识点?这是新手们刚接触编程时常见的问题;下面golang学习网就来给大家整理分享一些知识点,希望能够给初学者一些帮助。本篇文章就来介绍《Go语言排序(借助sort.Interface接口)》,涉及到接口,有需要的可以收藏一下
排序操作和字符串格式化一样是很多程序经常使用的操作。尽管一个最短的快排程序只要 15 行就可以搞定,但是一个健壮的实现需要更多的代码,并且我们不希望每次我们需要的时候都重写或者拷贝这些代码。幸运的是,sort 包内置的提供了根据一些排序函数来对任何序列排序的功能。它的设计非常独到。在很多语言中,排序算法都是和序列数据类型关联,同时排序函数和具体类型元素关联。
相比之下,Go语言的 sort.Sort 函数不会对具体的序列和它的元素做任何假设。相反,它使用了一个接口类型 sort.Interface 来指定通用的排序算法和可能被排序到的序列类型之间的约定。这个接口的实现由序列的具体表示和它希望排序的元素决定,序列的表示经常是一个切片。
一个内置的排序算法需要知道三个东西:序列的长度,表示两个元素比较的结果,一种交换两个元素的方式;这就是 sort.Interface 的三个方法:
package sorttype Interface interface { Len() int // 获取元素数量 Less(i, j int) bool // i,j是序列元素的指数。 Swap(i, j int) // 交换元素}为了对序列进行排序,我们需要定义一个实现了这三个方法的类型,然后对这个类型的一个实例应用 sort.Sort 函数。思考对一个字符串切片进行排序,这可能是最简单的例子了。下面是这个新的类型 MyStringList 和它的 Len,Less 和 Swap 方法
type MyStringList []stringfunc (p MyStringList ) Len() int { return len(m) }func (p MyStringList ) Less(i, j int) bool { return m[i]
使用sort.Interface接口进行排序
对一系列字符串进行排序时,使用字符串切片([]string)承载多个字符串。使用 type 关键字,将字符串切片([]string)定义为自定义类型 MyStringList。为了让 sort 包能识别 MyStringList,能够对 MyStringList 进行排序,就必须让 MyStringList 实现 sort.Interface 接口。下面是对字符串排序的详细代码(代码1):
package mainimport ( "fmt" "sort")// 将[]string定义为MyStringList类型type MyStringList []string// 实现sort.Interface接口的获取元素数量方法func (m MyStringList) Len() int { return len(m)}// 实现sort.Interface接口的比较元素方法func (m MyStringList) Less(i, j int) bool { return m[i] 代码输出结果:
1. First Blood
2. Double Kill
3. Triple Kill
4. Quadra Kill
5. Penta Kill
代码说明如下:第 9 行,接口实现不受限于结构体,任何类型都可以实现接口。要排序的字符串切片 []string 是系统定制好的类型,无法让这个类型去实现 sort.Interface 排序接口。因此,需要将 []string 定义为自定义的类型。第 12 行,实现获取元素数量的 Len() 方法,返回字符串切片的元素数量。第 17 行,实现比较元素的 Less() 方法,直接取 m 切片的 i 和 j 元素值进行小于比较,并返回比较结果。第 22 行,实现交换元素的 Swap() 方法,这里使用 Go语言的多变量赋值特性实现元素交换。第 29 行,由于将 []string 定义成 MyStringList 类型,字符串切片初始化的过程等效于下面的写法:
names := []string { "3. Triple Kill", "5. Penta Kill", "2. Double Kill", "4. Quadra Kill", "1. First Blood",}第 38 行,使用 sort 包的 Sort() 函数,将 names(MyStringList类型)进行排序。排序时,sort 包会通过 MyStringList 实现的 Len()、Less()、Swap() 这 3 个方法进行数据获取和修改。第 41 行,遍历排序好的字符串切片,并打印结果。
常见类型的便捷排序
通过实现 sort.Interface 接口的排序过程具有很强的可定制性,可以根据被排序对象比较复杂的特性进行定制。例如,需要多种排序逻辑的需求就适合使用 sort.Interface 接口进行排序。但大部分情况中,只需要对字符串、整型等进行快速排序。Go语言中提供了一些固定模式的封装以方便开发者迅速对内容进行排序。1) 字符串切片的便捷排序
sort 包中有一个 StringSlice 类型,定义如下:type StringSlice []stringfunc (p StringSlice) Len() int { return len(p) }func (p StringSlice) Less(i, j int) bool { return p[i] sort 包中的 StringSlice 的代码与 MyStringList 的实现代码几乎一样。因此,只需要使用 sort 包的 StringSlice 就可以更简单快速地进行字符串排序。将代码1中的排序代码简化后如下所示:
names := sort.StringSlice{ "3. Triple Kill", "5. Penta Kill", "2. Double Kill", "4. Quadra Kill", "1. First Blood",}sort.Sort(names)简化后,只要两句代码就实现了字符串排序的功能。
2) 对整型切片进行排序
除了字符串可以使用 sort 包进行便捷排序外,还可以使用 sort.IntSlice 进行整型切片的排序。sort.IntSlice 的定义如下:type IntSlice []intfunc (p IntSlice) Len() int { return len(p) }func (p IntSlice) Less(i, j int) bool { return p[i] sort 包在 sort.Interface 对各类型的封装上还有更进一步的简化,下面使用 sort.Strings 继续对代码1进行简化,代码如下:
names := []string{ "3. Triple Kill", "5. Penta Kill", "2. Double Kill", "4. Quadra Kill", "1. First Blood",}sort.Strings(names)// 遍历打印结果for _, v := range names { fmt.Printf("%s", v)}代码说明如下:第 1 行,需要排序的字符串切片。第 9 行,使用 sort.Strings 直接对字符串切片进行排序。
3) sort包内建的类型排序接口一览
Go语言中的 sort 包中定义了一些常见类型的排序方法,如下表所示。编程中经常用到的 int32、int64、float32、bool 类型并没有由 sort 包实现,使用时依然需要开发者自己编写。
对结构体数据进行排序
除了基本类型的排序,也可以对结构体进行排序。结构体比基本类型更为复杂,排序时不能像数值和字符串一样拥有一些固定的单一原则。结构体的多个字段在排序中可能会存在多种排序的规则,例如,结构体中的名字按字母升序排列,数值按从小到大的顺序排序。一般在多种规则同时存在时,需要确定规则的优先度,如先按名字排序,再按年龄排序等。1) 完整实现sort.Interface进行结构体排序
将一批英雄名单使用结构体定义,英雄名单的结构体中定义了英雄的名字和分类。排序时要求按照英雄的分类进行排序,相同分类的情况下按名字进行排序,详细代码实现过程如下。结构体排序代码(代码2):
package mainimport ( "fmt" "sort")// 声明英雄的分类type HeroKind int// 定义HeroKind常量, 类似于枚举const ( None HeroKind = iota Tank Assassin Mage)// 定义英雄名单的结构type Hero struct { Name string // 英雄的名字 Kind HeroKind // 英雄的种类}// 将英雄指针的切片定义为Heros类型type Heros []*Hero// 实现sort.Interface接口取元素数量方法func (s Heros) Len() int { return len(s)}// 实现sort.Interface接口比较元素方法func (s Heros) Less(i, j int) bool { // 如果英雄的分类不一致时, 优先对分类进行排序 if s[i].Kind != s[j].Kind { return s[i].Kind 代码输出如下:
&{Name:关羽 Kind:1}
&{Name:吕布 Kind:1}
&{Name:李白 Kind:2}
&{Name:貂蝉 Kind:2}
&{Name:妲己 Kind:3}
&{Name:诸葛亮 Kind:3}
代码说明如下:第 9 行,将 int 声明为 HeroKind 英雄类型,后面会将这个类型当做枚举来使用。第 13 行,定义一些英雄类型常量,可以理解为枚举的值。第 26 行,为了方便实现 sort.Interface 接口,将 []*Hero 定义为 Heros 类型。第 29 行,Heros 类型实现了 sort.Interface 的 Len() 方法,返回英雄的数量。第 34 行,Heros 类型实现了 sort.Interface 的 Less() 方法,根据英雄字段的比较结果决定如何排序。第 37 行,当英雄的分类不一致时,优先按分类的枚举数值从小到大排序。第 42 行,英雄分类相等的情况下,默认根据英雄的名字字符升序排序。第 46 行,Heros 类型实现了 sort.Interface 的 Swap() 方法,交换英雄元素的位置。第 53~60 行,准备一系列英雄数据。第 63 行,使用 sort 包进行排序。第 66 行,遍历所有排序完成的英雄数据。
2) 使用sort.Slice进行切片元素排序
从 Go 1.8 开始,Go语言在 sort 包中提供了 sort.Slice() 函数进行更为简便的排序方法。sort.Slice() 函数只要求传入需要排序的数据,以及一个排序时对元素的回调函数,类型为 func(i,j int)bool,sort.Slice() 函数的定义如下:func Slice(slice interface{}, less func(i, j int) bool)使用 sort.Slice() 函数,对代码2重新优化的完整代码如下:
package mainimport ( "fmt" "sort")type HeroKind intconst ( None = iota Tank Assassin Mage)type Hero struct { Name string Kind HeroKind}func main() { heros := []*Hero{ {"吕布", Tank}, {"李白", Assassin}, {"妲己", Mage}, {"貂蝉", Assassin}, {"关羽", Tank}, {"诸葛亮", Mage}, } sort.Slice(heros, func(i, j int) bool { if heros[i].Kind != heros[j].Kind { return heros[i].Kind 第 33 行到第 39 行加粗部分是新添加的 sort.Slice() 及回调函数部分。对比前面的代码,这里去掉了 Heros 及接口实现部分的代码。
使用 sort.Slice() 不仅可以完成结构体切片排序,还可以对各种切片类型进行自定义排序。
本篇关于《Go语言排序(借助sort.Interface接口)》的介绍就到此结束啦,但是学无止境,想要了解学习更多关于Golang的相关知识,请关注golang学习网公众号!
-
250 收藏
-
462 收藏
-
394 收藏
-
256 收藏
-
282 收藏
-
108 收藏
-
367 收藏
-
419 收藏
-
234 收藏
-
155 收藏
-
457 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 立即学习 542次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 立即学习 507次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 立即学习 497次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 立即学习 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 立即学习 484次学习
-
- 虚幻的樱桃
- 太给力了,一直没懂这个问题,但其实工作中常常有遇到...不过今天到这,帮助很大,总算是懂了,感谢老哥分享博文!
- 2023-03-05 03:14:46
-
- 土豪的饼干
- 很有用,一直没懂这个问题,但其实工作中常常有遇到...不过今天到这,看完之后很有帮助,总算是懂了,感谢大佬分享技术文章!
- 2023-02-10 22:58:51
-
- 傻傻的口红
- 这篇文章内容太及时了,好细啊,赞 👍👍,收藏了,关注up主了!希望up主能多写Golang相关的文章。
- 2023-01-23 20:10:03
-
- 干净的蛋挞
- 这篇技术贴真及时,好细啊,很好,收藏了,关注作者大大了!希望作者大大能多写Golang相关的文章。
- 2023-01-06 04:29:46
-
- 高挑的硬币
- 这篇技术文章真及时,太详细了,感谢大佬分享,已收藏,关注up主了!希望up主能多写Golang相关的文章。
- 2022-12-29 07:41:07