C# 中的List.Sort()--集合排序方法全面解析
在C#中,List.Sort()不仅为我们提供了默认的排序方法,还为我们提供了4种自定义排序的方法,通过默认排序方法,我们无需重写任何Sort()方法的实现代码,就能对单参数类型的List数据进行单一规则的排序,如果通过对这些方法进行改进我们可以轻松做到对多参数、多规则的复杂排序。
下面是C#自定义排序的4种方法:
List.Sort(); List .Sort(IComparer Comparer); List .Sort(intindex,intcount,IComparer Comparer); List .Sort(Comparison comparison);
实现目标
假设存在一个People类,包含Name、Age属性,在客户端中创建List保存多个实例,希望对List中的内容根据Name和Age参数进行排序,排序规则为,先按姓名升序排序,如果姓名相同再按年龄的升序排序:
classPeople { publicPeople(stringname,intage){Name=name;Age=age;} publicstringName{get;set;}//姓名 publicintAge{get;set;}//年龄 } //客户端 classClient { staticvoidMain(string[]args) { ListpeopleList=newList (); peopleList.Add(newPeople("张三",22)); peopleList.Add(newPeople("张三",24)); peopleList.Add(newPeople("李四",18)); peopleList.Add(newPeople("王五",16)); peopleList.Add(newPeople("王五",30)); } }
方法一、对People类继承IComparable接口,实现CompareTo()方法
该方法为系统默认的方法,单一参数时会默认进行升序排序。但遇到多参数(Name、Age)排序时,我们需要对该默认方法进行修改。
方法一:People类继承IComparable接口,实现CompareTo()方法
IComparable
原理:自行实现的CompareTo()方法会在list.Sort()内部进行元素两两比较,最终实现排序
classPeople:IComparable{ publicPeople(stringname,intage){Name=name;Age=age;} publicstringName{get;set;} publicintAge{get;set;} //list.Sort()时会根据该CompareTo()进行自定义比较 publicintCompareTo(Peopleother) { if(this.Name!=other.Name) { returnthis.Name.CompareTo(other.Name); } elseif(this.Age!=other.Age) { returnthis.Age.CompareTo(other.Age); } elsereturn0; } } //客户端 peopleList.Sort(); //OUTPUT: //李四18 //王五16 //王五30 //张三22 //张三24
方法二:增加People类的外部比较类,继承IComparer接口、实现Compare()方法
区别于上述继承IComparable的方法,该方法不可在People内继承实现IComparer接口,而是需要新建比较方法类进行接口实现
方法二:新建PeopleComparer类、继承IComparer接口、实现Compare()方法
原理:list.Sort()将PeopleComparer类的实例作为参数,在内部使用Compare()方法进行两两比较,最终实现排序(注:上述方法为CompareTo(),此处为Compare()方法)
//自定义比较方法类 classPeopleComparer:IComparer{ //区别于CompareTo()单参数,此处为双参数 publicintCompare(Peoplex,Peopley) { if(x.Name!=y.Name) { returnx.Name.CompareTo(y.Name); } elseif(x.Age!=y.Age) { returnx.Age.CompareTo(y.Age); } elsereturn0; } } //客户端 //传入参数为自定义比较类的实例 peopleList.Sort(newPeopleComparer()); //OUTPUT: //李四18 //王五16 //王五30 //张三22 //张三24
同理,List
方法三、采用泛型委托Comparison,绑定自定义的比较方法
区别于上述继承接口的方法,此方法的参数为泛型委托Comparison
委托原型:publicdelegateintComparison
方法三:依照委托的使用方法,首先创建委托实例MyComparison,并绑定到自定义的比较方法PeopleComparison()上,最终调用list.Sort()时将委托实例传入
原理:list.Sort()根据传入的委托方法,进行两两元素比较最终实现排序
//客户端 classClient { //方法0自定义比较方法 publicstaticintPeopleComparison(Peoplep1,Peoplep2) { if(p1.Name!=p2.Name) { returnp1.Name.CompareTo(p2.Name); } elseif(p1.Age!=p2.Age) { returnp1.Age.CompareTo(p2.Age); } elsereturn0; } staticvoidMain(string[]args) { /创建list.../ //方法0创建委托实例并绑定 ComparisonMyComparison=PeopleComparison; //传入该实例实现比较方法 peopleList.Sort(MyComparison); //OUTPUT: //李四18 //王五16 //王五30 //张三22 //张三24 } }
此外,既然Comparison
//Lambda表达式实现Comparison委托 peopleList.Sort((p1,p2)=> { if(p1.Name!=p2.Name) { returnp2.Name.CompareTo(p1.Name); } elseif(p1.Age!=p2.Age) { returnp2.Age.CompareTo(p1.Age); } elsereturn0; }); //OUTPUT: //张三24 //张三22 //王五30 //王五16 //李四18
总结
虽然本文仅使用了List
两种接口:IComparable
泛型委托:Comparison
参考
IComparable接口-Microsoft
Comparison委托-Microsoft
IComparer接口-Microsoft
附:一个完整的测试Demo
usingSystem; usingSystem.Collections.Generic; usingSystem.Linq; usingSystem.Text; namespaceListSort { classProgram { staticvoidDisplayInfo(List list){ //输出List元素内容 foreach(variteminlist){ System.Console.Write("{0}",item.ToString()); } System.Console.WriteLine(""); } //方法3自定义委托泛型比较方法 publicstaticintPeopleComparison(Peoplep1,Peoplep2) { if(p1.Name!=p2.Name) { returnp1.Name.CompareTo(p2.Name); } elseif(p1.Age!=p2.Age) { returnp1.Age.CompareTo(p2.Age); } elsereturn0; } staticvoidMain(string[]args) { List peopleList=newList (); peopleList.Add(newPeople("张三",22)); peopleList.Add(newPeople("张三",24)); peopleList.Add(newPeople("李四",18)); peopleList.Add(newPeople("王五",16)); peopleList.Add(newPeople("王五",30)); System.Console.WriteLine("排序前原始数据:"); DisplayInfo(peopleList); System.Console.WriteLine("------------------------------------"); System.Console.WriteLine("方法1排序后数据:"); peopleList.Sort(); DisplayInfo(peopleList); System.Console.WriteLine("方法2排序后数据:"); DisplayInfo(peopleList); //方法1使用IComparer 接口。 peopleList.Sort(newPeopleComparer()); //方法2除以上两种方法以外还可以使用另一种方法,在People类中实现IComparable peopleList.Sort(); System.Console.WriteLine("方法3排序后数据:"); DisplayInfo(peopleList); //方法3创建泛型委托实例并绑定 Comparison MyComparison=PeopleComparison; //传入该实例实现比较方法 peopleList.Sort(MyComparison); System.Console.WriteLine("方法3排序后数据:"); DisplayInfo(peopleList); //方法3使用Comparison 委托,Lambda写法 peopleList.Sort((left,right)=> { //先按姓名排序,如果姓名相同再按年龄排序 intx=left.Name.CompareTo(right.Name); if(x==0){ if(left.Age>right.Age) x=1; elseif(left.Age==right.Age) x=0; else x=-1; } returnx; }); } } //方法一 publicclassPeople:IComparable { publicintAge{get;set;} publicstringName{get;set;} publicPeople(stringname,intage){ this.Name=name; this.Age=age; } publicoverridestringToString(){ stringresult=""; result="["+this.Name+","+this.Age.ToString()+"]"; returnresult; } publicintCompareTo(Peopleother) { intx=this.Name.CompareTo(other.Name); if(x==0){ if(this.Age>other.Age) x=1; elseif(this.Age==other.Age) x=0; else x=-1; } returnx; } } //方法二 publicclassPeopleComparer:IComparer { publicintCompare(Peopleleft,Peopleright) { intx=left.Name.CompareTo(right.Name); if(x==0){ if(left.Age>right.Age) x=1; elseif(left.Age==right.Age) x=0; else x=-1; } returnx; } } }
补充:C#IComparable和IComparer接口和自定义比较器
前言
ArrayList里面有一个方法:
publicvirtualvoidSort(IComparercomparer);
使用指定的比较器对整个System.Collections.ArrayList中的元素进行排序。
comparer:比较元素时要使用的System.Collections.IComparer实现。
啥玩意啊?
正文
1.Comparer类简单介绍
想弄清楚这个,我们先来看看这么一个类。
在System.Collections名称空间中,有这么一个类:Comparer。顾名思义,他可以实现对简单类型的比较,什么意思呢?来看如下代码:
inta=1,b=2;
正常情况下,我们要怎样比较他们的大小?if,运算符,……?这当然可以,不过Comparer已经给我们提供了一个函数,可以直接使用:(需要usingSystem.Collections;)
Console.WriteLine(Comparer.Default.Compare(a,b));
因为a
这里通过Comparer里的静态属性Default获得Comparer的实例调用了Comparer里的非静态函数Compare。
(还可以比较根据字母比较两个string类型,这里就省略介绍了)
2.自定义比较器,IComparable,IComparer接口
当然,这个类不仅仅只是用来比较两个数的大小的。有时候我们想直接比较两个对象,但是引用里面的属性或许比较麻烦。尤其是参考要素过多,不好直接比较的时候,怎样才能更高效地比较两个对象呢?这时候,我们就需要自定义比较器了。
首先来介绍IComparable接口。这个接口里只有一个方法CompareTo()。让你的类实现这个接口的CompareTo方法,就可以直接调用这个方法和另一个对象比较。下面是例子:
publicclassClassTest:IComparable { publicintintTest; publicintCompareTo(objectobj) { returnintTest-((ClassTest)obj).intTest; //这里的代码可以按需要自己编写,这里只是一个简单的示例 } }
然后就可以直接使用啦:
ClassTesta=newClassTest(){intTest=1}; ClassTestb=newClassTest(){intTest=2}; Console.WriteLine(a.CompareTo(b));//输出-1 Comparer类已经为我们提供了IComparer的默认实现,但我们仍然可以自定义它。新建一个类:(记得usingSystem.Collections;) publicclassClassTestComparer:IComparer { publicstaticIComparerDefault=newClassTestComparer(); //这里必须使用这样的定义,将对象转化为IComparer类型有很大用处,下面会介绍 publicintCompare(objecta,objectb) { return((ClassTest)a).intTest-((ClassTest)b).intTest; //同样这里使用最简单的示例,但是你可以大放异彩 } }
注意,如果用于比较的类和设定的类不一样,就会出现错误。
使用示例:
ClassTesta=newClassTest(){intTest=1}; ClassTestb=newClassTest(){intTest=2}; Console.WriteLine(ClassTestComparer.Default.Compare(a,b)); //结果是-1
可以发现,这两个接口的不同之处在于:IComparable在要比较的对象的类中实现,可以比较该对象和另一个对象。IComparer在一个单独的类中实现,可以比较任意两个对象(关键是你的设置)。
3.对集合排序
当然,这两个接口还有更强大的用处。我们可以使用这两个接口对集合进行排序。还记得前言里的Sort()方法吗?接下来就以ArrayList为例,介绍如何使用。
ArrayListClassTests=newArrayList(); ClassTesta=newClassTest(){intTest=1}; ClassTestb=newClassTest(){intTest=2}; ClassTestc=newClassTest(){intTest=3}; ClassTests.Add(a); ClassTests.Add(b); ClassTests.Add(c); ClassTests.Sort(); //使用无参的Sort,将调用类中的CompareTo()方法,因为ClassTest实现了这个方法,所以是可以调用的。如果没有实现,编译器会报错。 ClassTests.Sort(ClassTestComparer.Default); //这将使用Compare()方法对集合中的元素排序。ClassTestComparer类实现了这个方法,并且提供了一个IComparer类型的属性。
需要注意的是:
两个接口提供的方法返回值都是int类型的,负数代表小于,0代表等于,正数代表大于。所以对数字之外的自定义比较器,需要人工设定什么是“大”,什么是“小”。所以上文示例中两个数直接相减,就可以比较大小。
排序完之后,按照返回的int值,集合是由小到大排列的。
使用无参Sort()时,集合中至少要有一个类实现了IComparable,否则会报错。
一般来说,都是对同一个类进行比较。不过,也可以实现对不同类比较的代码,这就看具体需要了。
以上为个人经验,希望能给大家一个参考,也希望大家多多支持毛票票。如有错误或未考虑完全的地方,望不吝赐教。
声明:本文内容来源于网络,版权归原作者所有,内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎发送邮件至:czq8825#qq.com(发邮件时,请将#更换为@)进行举报,并提供相关证据,一经查实,本站将立刻删除涉嫌侵权内容。