golang对自定义类型进行排序的解决方法

前言

Go 语言支持我们自定义类型,我们大家在实际项目中,常常需要根据一个结构体类型的某个字段进行排序。之前遇到这个问题不知道如何解决,后来在网上搜索了相关问题,找到了一些好的解决方案,此处参考下,做个总结吧。

由于 golang 的 sort 包本身就提供了相应的功能, 我们就没必要重复的造个轮子了,来看看如何利用 sort 包来实现吧。

sort包浅谈

golang中也实现了排序算法的包sort包,sort 包 在内部实现了四种基本的排序算法:插入排序(insertionSort)、归并排序(symMerge)、堆排序(heapSort)和快速排序(quickSort); sort 包会依据实际数据自动选择最优的排序算法。

所以我们写代码时只需要考虑实现 sort.Interface 这个类型就可以了。

粗略的看看sort包

func Sort(data Interface) {
 // Switch to heapsort if depth of 2*ceil(lg(n+1)) is reached.
 n := data.Len()
 maxDepth := 0
 for i := n; i > 0; i >>= 1 {
 maxDepth++
 }
 maxDepth *= 2
 quickSort(data, 0, n, maxDepth)
}
type Interface interface {
 // Len is the number of elements in the collection.
 Len() int
 // Less reports whether the element with
 // index i should sort before the element with index j.
 Less(i, j int) bool
 // Swap swaps the elements with indexes i and j.
 Swap(i, j int)
}
// 内部实现的四种排序算法
// 插入排序
func insertionSort(data Interface, a, b int)
// 堆排序
func heapSort(data Interface, a, b int)
// 快速排序
func quickSort(data Interface, a, b, maxDepth int)
// 归并排序
func symMerge(data Interface, a, m, b int)

所以要调用sort.Sort() 来实现自定义类型排序,只需要我们的类型实现 Interface 接口类型中的三个方法即可。

先看看 sort 包本身对于 []int 类型如何排序

// 首先定义了一个[]int类型的别名IntSlice
type IntSlice []int
// 获取此 slice 的长度
func (p IntSlice) Len() int   { return len(p) }
// 比较两个元素大小 升序
func (p IntSlice) Less(i, j int) bool { return p[i] < p[j] }
// 交换数据
func (p IntSlice) Swap(i, j int)  { p[i], p[j] = p[j], p[i] }
// sort.Ints()内部调用Sort() 方法实现排序
// 注意 要先将[]int 转换为 IntSlice类型 因为此类型才实现了Interface的三个方法
func Ints(a []int) { Sort(IntSlice(a)) }

照葫芦画瓢 我们来对自定义的结构体类型进行降序排序

package main
import (
 "fmt"
 "sort"
)
type Person struct {
 Name string
 Age int
}
type Persons []Person
// 获取此 slice 的长度
func (p Persons) Len() int { return len(p) }
// 根据元素的年龄降序排序 (此处按照自己的业务逻辑写)
func (p Persons) Less(i, j int) bool {
 return p[i].Age > p[j].Age
}
// 交换数据
func (p Persons) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
func main() {
 persons := Persons{
 {
 Name: "test1",
 Age: 20,
 },
 {
 Name: "test2",
 Age: 22,
 },
 {
 Name: "test3",
 Age: 21,
 },
 }
 fmt.Println("排序前")
 for _, person := range persons {
 fmt.Println(person.Name, ":", person.Age)
 }
 sort.Sort(persons)
 fmt.Println("排序后")
 for _, person := range persons {
 fmt.Println(person.Name, ":", person.Age)
 }
}

其实,一般 Len()Swap() 基本不做改变,只有涉及到元素比较的 Less() 方法会有所改变。

当我们对某一个结构体中多个字段进行排序时怎么办,难道每排序一个就写下这三个方法么,当然不是。我们可以利用嵌套结构体来解决这个问题。因为嵌套结构体可以继承父结构体的所有属性和方法

比如我想对上面 Person 的 Name 字段和 Age 对要排序,我们可以利用嵌套结构体来改进一下。

package main
import (
 "fmt"
 "sort"
)
type Person struct {
 Name string
 Age int
}
type Persons []Person
// Len()方法和Swap()方法不用变化
// 获取此 slice 的长度
func (p Persons) Len() int { return len(p) }
// 交换数据
func (p Persons) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
// 嵌套结构体 将继承 Person 的所有属性和方法
// 所以相当于SortByName 也实现了 Len() 和 Swap() 方法
type SortByName struct{ Persons }
// 根据元素的姓名长度降序排序 (此处按照自己的业务逻辑写)
func (p SortByName) Less(i, j int) bool {
 return len(p.Persons[i].Name) > len(p.Persons[j].Name)
}
type SortByAge struct{ Persons }
// 根据元素的年龄降序排序 (此处按照自己的业务逻辑写)
func (p SortByAge) Less(i, j int) bool {
 return p.Persons[i].Age > p.Persons[j].Age
}
func main() {
 persons := Persons{
 {
 Name: "test123",
 Age: 20,
 },
 {
 Name: "test1",
 Age: 22,
 },
 {
 Name: "test12",
 Age: 21,
 },
 }
 fmt.Println("排序前")
 for _, person := range persons {
 fmt.Println(person.Name, ":", person.Age)
 }
 sort.Sort(SortByName{persons})
 fmt.Println("排序后")
 for _, person := range persons {
 fmt.Println(person.Name, ":", person.Age)
 }
}

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对我们的支持。

(0)

相关推荐

  • Golang中的自定义函数详解

    不管是面向过程的编程,还是面向对象的编程,都离不开函数的概念,分别是,参数,函数名,返回值.接下来我们看看Go语言在这三个方面是做怎么操作的吧. 参数 谈到参数,我们在写函数或者是类中的方法的时候都需要考虑我们应该传递怎样的参数,或者是是否需要参数. 参数首先分为无参函数有参.无参也就是没有参数,也就不用写了. 有参 func functionTest() {  # 小括号内就是用来放参数的     # 函数体内 } Go语言是强数据类型的语言,参数是要指定类型的不然就报错.func 是函数的声

  • goLang引入自定义包的方法

    看完golang的基本语法后,为了模块化编程,试用了下golang的包管理,结果真踩了几个坑,总结一下吧. 一. 设置$GOPATH环境变量 golang和C或php不一样,不会自动查找当前路径下的文件,必须先在$GOAPTH里添加自己工程的路径: 二. 自定义包里面对外提供的API函数,首字母必须大写 如:func api() string {return "hello api!"}则不能被包外的函数检测到: 应改为:func Api() string {return "h

  • Go语言之自定义集合Set

    一.Go语言实战--自定义集合Set 在Go语言中有作为Hash Table实现的字典(Map)类型,但标准数据类型中并没有集合(Set)这种数据类型.比较 Set 和 Map 的主要特性,有类似特性如下: 它们中的元素都是不可重复的. 它们都只能用迭代的方式取出其中的所有元素. 对它们中的元素进行迭代的顺序都是与元素插入顺序无关的,同时也不保证任何有序性. 但是,它们之间也有一些区别,如下: Set 的元素是一个单一的值,而 Map 的元素则是一个键值对. Set 的元素不可重复指的是不能存在

  • golang对自定义类型进行排序的解决方法

    前言 Go 语言支持我们自定义类型,我们大家在实际项目中,常常需要根据一个结构体类型的某个字段进行排序.之前遇到这个问题不知道如何解决,后来在网上搜索了相关问题,找到了一些好的解决方案,此处参考下,做个总结吧. 由于 golang 的 sort 包本身就提供了相应的功能, 我们就没必要重复的造个轮子了,来看看如何利用 sort 包来实现吧. sort包浅谈 golang中也实现了排序算法的包sort包,sort 包 在内部实现了四种基本的排序算法:插入排序(insertionSort).归并排序

  • Golang解析JSON遇到的坑及解决方法

    目录 写在前面 空指针会被解析成字符串"null" int类型会被解析成float64 写在前面 在写go的时候经常用到序列化.反序列化,记录一下遇到过的坑. 空指针会被解析成字符串"null" type Person struct { Name string Age int } func main() { var p *Person bytes, err := json.Marshal(p) checkError(err) fmt.Printf("len

  • golang网络socket粘包问题的解决方法

    本文实例讲述了golang网络socket粘包问题的解决方法.分享给大家供大家参考,具体如下: 看到很多人问这个问题, 今天就写了个例子, 希望能帮助大家 首先说一下什么是粘包:百度上比较通俗的说法是指TCP协议中,发送方发送的若干包数据到接收方接收时粘成一包,从接收缓冲区看,后一包数据的头紧接着前一包数据的尾. 解决方案如下: 服务端: 复制代码 代码如下: package main import (     "bytes"     "encoding/binary&quo

  • implicit关键字做自定义类型隐式转换的方法

    隐式转换可以通过消除不必要的类型转换来提高源代码的可读性.但是,因为隐式转换不需要程序员将一种类型显式强制转换 为另一种类型,所以使用隐式转换时必须格外小心,以免出现意外结果.一般情况下,隐式转换运算符应当从不引发异常并且 从不丢失信息,以便可以在程序员不知晓的情况下安全使用它们. 在C#中,implicit关键字可以用来做自定义类型隐式转换.下面给个例子来说明. 定义一个Point类,表示一个点: 复制代码 代码如下: public class Point   {     public dou

  • 自定义AlertDialog去除黑色背景的解决方法

    主要注意的是在资源引用的地方 AlertDialog.Builder(this,R.style.dialogNoBg).create(); 这里要自定义sytle <!--dialog去阴影--> <style name="dialogNoBg"> <item name="android:background">#00000000</item> <item name="android:windowBac

  • 详解Java泛型中类型擦除问题的解决方法

    以前就了解过Java泛型的实现是不完整的,最近在做一些代码重构的时候遇到一些Java泛型类型擦除的问题,简单的来说,Java泛型中所指定的类型在编译时会将其去除,因此List 和 List 在编译成字节码的时候实际上是一样的.因此java泛型只能做到编译期检查的功能,运行期间就不能保证类型安全.我最近遇到的一个问题如下: 假设有两个bean类 /** Test. */ @Data @NoArgsConstructor @AllArgsConstructor public static class

  • Oracle在表中有数据的情况下修改字段类型或长度的解决方法

    Oracle 数据库如何在表中有数据的情况下,修改表字段的类型或者增加表字段的长度解决方法 我们偶尔需要在已有表,并且有数据的情况下,修改其某个字段的类型或改变他的长度,但是因为表中有数据,所以不可以直接修改,需要换个思路. -- Create table create table TABLE1 ( col1 number(9), col2 char(20) ); -- 尝试修改 -- 修改成功,因为表中无数据 ALTER TABLE TABLE1 MODIFY COL1 NUMBER(10);

  • Golang TCP粘包拆包问题的解决方法

    什么是粘包问题 最近在使用Golang编写Socket层,发现有时候接收端会一次读到多个数据包的问题.于是通过查阅资料,发现这个就是传说中的TCP粘包问题.下面通过编写代码来重现这个问题: 服务端代码 server/main.go func main() { l, err := net.Listen("tcp", ":4044") if err != nil { panic(err) } fmt.Println("listen to 4044")

  • sqlServer使用ROW_NUMBER时不排序的解决方法

    设置sqlServer使用ROW_NUMBER时不排序 --1.看到NHibernate是这样写的分页,感觉写起来比较容易理解(应该不会有效率问题吧?) --with只是定一个别名? [sql] with query as (select ROW_NUMBER() over(order by (select 0)) AS ROWNUM, * FROM Product) select * from query where ROWNUM BETWEEN 5 AND 10 --2.ROW_NUMBER

  • 用php实现选择排序的解决方法

    1,定义:选择排序(Selection sort)是一种简单直观的排序算法.它的工作原理如下.首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾.以此类推,直到所有元素均排序完毕. 参考代码: 复制代码 代码如下: <?php    //选择排序(Selection sort)是一种简单直观的排序算法.它的工作原理如下.首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中

随机推荐