.Net中的集合排序可以这么玩你知道吗
c#集合类型概述
集合是.net fcl(framework class library)中很重要的一部分。所有的集合类都继承自ienumerable。集合类总体可分为一下几类:关联/非关联型集合,顺序/随机访问集合,顺序/无序集合,泛型/非泛型集合,线程安全集合。
各集合类底层接口关系图
背景:
public class stockquantity { public stockquantity(string status, datetime datetime, int quantity) { status = status; datetime = datetime; quantity = quantity; } public string status { get; set; } public datetime datetime { get; set; } public int quantity { get; set; } }
该对象,主要有三个字段,现在的业务需求是,取到了一个类型为list<stockquantity>集合stockquantities,需要对该集合进行三次排序,排序规则及优先级如下:
1. status为空的排在后面,不为空的排在前面,不关心status的内容,只关心status是否为空。
2. datetime升序排序。
3. quantity升序排序。
小白我的做法:
我只知道可以对集合用oderby排序,对以上三条规则,所以设计思路如下。
1. stockquantities.orderby(u=>u.status)
错误,
该排序得规则不仅仅会考虑status是否为空,还会考虑status的内容。
如果status是[“b”,”c”,null,”d”]
,那么排序结果是[null,“b”,”c”,”d”]
。
而我们要的结果是[“b”,”c”,”d” ,null]
(直接把null的丢到最后,别的不动)
怎么办?
暂时不知道,先不管
2. 对datetime进行升序排序,这简单
stockquantities.orderby(u=>u.datetime)
半对!
为什么半对,看下面
3. 在排序2的前提下,用orderby,也就是stockquantities.orderby(u=>u.datetime).orderby(u=>u.quantity)
错误!
以上表达式等同于下面两条的表达式:
stockquantities = stockquantities.orderby(u=>u.datetime) stockquantities = stockquantities.orderby(u=>u.quantity)
所以第一条代码就是废代码,最终排序还是以quantity进行排序的。
虽然我是小白,但我还是明白这样是错误的,所以我的做法是
stockquantities = stockquantities.orderby(u => u.datetime).tolist(); foreach (var dateorder in stockquantities) { var datetimeorderby = stockquantities.where(u => u.datetime.date == dateorder.datetime.date) .orderby(u => u.count); foreach (var countorder in datetimeorderby) { if (countorder.output == false) { console.writeline($"{countorder.status}-{countorder.datetime}-{countorder.count}"); countorder.output = true; } } } console.readkey();
采用双层循环,先取到按时间排序的数据 dateorder,再去和该条数据在同一天的所有数据并对quantity进行排序,为了防止重复的输出,我同时给stockquantity对象加上了output属性,当该属性为false为,则输出该对象的内容,并把output属性设为true,这样就不会重复输出了,而且实现了先对datetime排序,再对quantity进行排序。
so easy!!
然而,当开心地把这样的代码提交之后,却被同事狠狠地鄙视了,说到:“什么烂代码啊!”然道还有比这更好的代码?
给同事倒了一杯茶,点了一根烟,虚心请教。
大佬做法:
同事给我讲了两招,分别是条件排序、多级排序。
什么是条件排序,怎么用?
1. stockquantities.orderby(u=>u.status==null)
这就是条件排序,可是咋一看,给人一种是把status为空的排前面,不为空的排后面的错觉。
其实不然,我们看到orderby里面的一个返回值为bool类型的表达式,该排序先排结果为0(false)的,再排结果为1(true)的。这种排序只考虑返回的bool值,不考虑参数的具体值,所以姑且称它为条件排序。
完全符合排序规则1的要求。
什么是多级排序,怎么用?
2. 利用我上面我的代码排序虽然可以实现先排datetime,再排quantity,但是该算法的时间复杂度的n*n,而且给stockquantity添加了output字段,明显是不科学的。
然而,连续地使用多个orderby最终只会生效最后一个orderby,天无绝人之路,所以这个时候应该使用thenby!!
使用thenby可以讲以上的三条排序规则简化如下:
stockquantities = stockquantities.orderby(u => u.status==null).thenby(u => u.datetime).thenby(u => u.quantity).tolist();
即可完美地实现再前一个排序前提下进行二级排序。
优化后的完整代码如下:
using system; using system.collections.generic; using system.linq; namespace orderby { class program { static void main(string[] args) { var stockquantities = new list<stockquantity>() { new stockquantity("正常品",new datetime(2017,4,16),12 ), new stockquantity("正常品",new datetime(2017,4,17),15 ), new stockquantity("残次品",new datetime(2017,4,16),10 ), new stockquantity("残次品",new datetime(2017,4,17),8 ), new stockquantity(null,new datetime(2017,4,18),8 ), }; stockquantities = stockquantities.orderby(u => u.status==null).thenby(u => u.datetime).thenby(u => u.quantity).tolist(); foreach (var stockquantity in stockquantities) { console.writeline($"{stockquantity.status}-{stockquantity.datetime}-{stockquantity.quantity}"); } console.readkey(); } } public class stockquantity { public stockquantity(string status, datetime datetime, int quantity) { status = status; datetime = datetime; quantity = quantity; } public string status { get; set; } public datetime datetime { get; set; } public int quantity { get; set; } } }
简单的一个排序优化,就把程序的时间复杂度从n*n降低到了n,所以在这里把这两种排序技巧分享出来,希望对不会的同学有所帮助。
总结
以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对的支持。
推荐阅读