接口最佳实践
sort包学习
1. sort对整形切片或者数组排序
平时写的整形切片排序:
var sl = []int{2, 4, 3, 1} sort.Ints(sl) fmt.Println(sl)
输出:[1 2 3 4]
我们看sort源码中有专门针对整形切片实现排序,例如:
sli := sort.IntSlice{2, 5, 3, 1} sli.Sort() fmt.Println(sli)
输出:[1 2 3 5]
我们看下源码实现:
// IntSlice attaches the methods of Interface to []int, sorting in increasing order. type IntSlice []int func (x IntSlice) Len() int { return len(x) } func (x IntSlice) Less(i, j int) bool { return x[i] < x[j] } func (x IntSlice) Swap(i, j int) { x[i], x[j] = x[j], x[i] } // Sort is a convenience method: x.Sort() calls Sort(x). func (x IntSlice) Sort() { Sort(x) }
我们发现IntSlice
类型实现了三个方法,而且最后Sort方法调用了函数Sort
,这个函数就是sort
包中真正核心意义上的排序函数,底层是快速排序实现的,我们可以看下:
// Sort sorts data. // It makes one call to data.Len to determine n and O(n*log(n)) calls to // data.Less and data.Swap. The sort is not guaranteed to be stable. func Sort(data Interface) { n := data.Len() quickSort(data, 0, n, maxDepth(n)) }
我们可以继续定位到quickSort
,但是这里重点介绍接口最佳实践,所以不再深入快速排序内部逻辑,大家下去自行研究。
我们看到Sort
函数参数是Interface接口,而这个接口定义如下:
type Interface interface { Len() int Less(i, j int) bool Swap(i, j int) }
啊哈,看到这里我们发现IntSlice是实现了Interface接口,所以它可以调用上述Sort
函数实现整形切片排序。
但是它这个排序是从小到大排序的,那如果从大到小实现排序呢?给各位留个思考题哈。
2. sort对结构体排序
因为对结构体的切片排序已经有相关函数实现了,比如看如下结构体:
// 学生 type StudentStruct struct { Name string Age int } type StudentSlice []StudentStruct func (ss StudentSlice) Len() int { return len(ss) } // Less 按照年龄排序 func (ss StudentSlice) Less(i, j int) bool { return ss[i].Age < ss[j].Age } func (ss StudentSlice) Swap(i, j int) { ss[i], ss[j] = ss[j], ss[i] }
用sort包中SliceStable
函数实现结构体切片排序:
var students StudentSlice for i:=0; i<10; i++ { ss := StudentStruct{ Name: fmt.Sprintf("姓名:%d", i), Age: rand.Intn(100), } students = append(students, ss) } sort.SliceStable(students, func(i, j int) bool { return students[i].Age < students[j].Age }) for _, v:=range students { fmt.Println(v) }
输出:
{姓名:9 0}
{姓名:5 18}
{姓名:6 25}
{姓名:7 40}
{姓名:2 47}
{姓名:8 56}
{姓名:3 59}
{姓名:0 81}
{姓名:4 81}
{姓名:1 87}
我们看到按照年龄已经排序了,但是如果我们要实现自己的排序规则怎么办呢?
答案很简单,自己实现接口:
type StudentSlice []StudentStruct func (ss StudentSlice) Len() int { return len(ss) } // Less 按照姓名从大到小排序 func (ss StudentSlice) Less(i, j int) bool { return ss[i].Name > ss[j].Name } func (ss StudentSlice) Swap(i, j int) { ss[i], ss[j] = ss[j], ss[i] } func main() { var students StudentSlice for i:=0; i<4; i++ { ss := StudentStruct{ Name: fmt.Sprintf("姓名:%d", i), Age: rand.Intn(100), } students = append(students, ss) } fmt.Println("排序前") for _, v:=range students { fmt.Println(v) } fmt.Println("排序后") sort.Sort(students) for _, v:=range students { fmt.Println(v) } }
输出:
排序前
{姓名:0 81}
{姓名:1 87}
{姓名:2 47}
{姓名:3 59}
排序后
{姓名:3 59}
{姓名:2 47}
{姓名:1 87}
{姓名:0 81}
我们看到排序已经是按照我们自己的排序规则排完序了,赞哦!
3. 小结
面向接口编程是一种很牛班的技术,它对底层设计者和开发者能力要求极高,所以一般复杂的底层infra建设都是有公司的大牛们担任,他们定义好接口之后对于技术小白来说你只要按照人家大牛的接口定义实现相应的方法即可,其他底层复杂的逻辑交给大牛们实现。当然如果有一天你写方法写累了,也想成为大牛搞搞底层建设,那么别着急,平时的日子里需要积累,相信吧,快乐的那一天将会来临!