温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

怎么在C# 中利用List.Sort()方法对集合进行排序

发布时间:2021-01-15 15:05:50 来源:亿速云 阅读:372 作者:Leah 栏目:开发技术

怎么在C# 中利用List.Sort()方法对集合进行排序?很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。

在C#中,List.Sort() 不仅为我们提供了默认的排序方法,还为我们提供了4种自定义排序的方法,通过默认排序方法,我们无需重写任何Sort()方法的实现代码,就能对单参数类型的List数据进行单一规则的排序,如果通过对这些方法进行改进我们可以轻松做到对多参数、多规则的复杂排序。

下面是C#自定义排序的4种方法:

List<T>.Sort();      List<T>.Sort(IComparer<T> Comparer); List<T>.Sort(int index, int count, IComparer<T> Comparer); List<T>.Sort(Comparison<T> comparison);

实现目标

假设存在一个People类,包含Name、Age属性,在客户端中创建List保存多个实例,希望对List中的内容根据Name和Age参数进行排序,排序规则为,先按姓名升序排序,如果姓名相同再按年龄的升序排序:

class People {   public People(string name, int age) { Name = name; Age = age; }   public string Name { get; set; } //姓名   public int Age { get; set; } //年龄 }   // 客户端 class Client {   static void Main(string[] args)   {     List<People> peopleList = new List<People>();     peopleList.Add(new People("张三", 22));     peopleList.Add(new People("张三", 24));     peopleList.Add(new People("李四", 18));     peopleList.Add(new People("王五", 16));     peopleList.Add(new People("王五", 30));   } }

方法一、对People类继承IComparable接口,实现CompareTo()方法

该方法为系统默认的方法,单一参数时会默认进行升序排序。但遇到多参数(Name、Age)排序时,我们需要对该默认方法进行修改。

方法一:People类继承IComparable接口,实现CompareTo()方法

IComparable<T>:定义由值类型或类实现的通用比较方法,旨在创建特定于类型的比较方法以对实例进行排序。

原理:自行实现的CompareTo()方法会在list.Sort()内部进行元素两两比较,最终实现排序

class People : IComparable<People> {   public People(string name, int age) { Name = name;Age = age; }   public string Name { get; set; }   public int Age { get; set; }     // list.Sort()时会根据该CompareTo()进行自定义比较   public int CompareTo(People other)   {     if (this.Name != other.Name)     {       return this.Name.CompareTo(other.Name);     }     else if (this.Age != other.Age)     {       return this.Age.CompareTo(other.Age);     }     else return 0;   } }   // 客户端 peopleList.Sort();   // OUTPUT: //   李四 18 //   王五 16 //   王五 30 //   张三 22 //   张三 24

方法二:增加People类的外部比较类,继承IComparer接口、实现Compare()方法

区别于上述继承IComparable的方法,该方法不可在People内继承实现IComparer接口,而是需要新建比较方法类进行接口实现

方法二:新建PeopleComparer类、继承IComparer接口、实现Compare()方法

原理:list.Sort()将PeopleComparer类的实例作为参数,在内部使用Compare()方法进行两两比较,最终实现排序(注:上述方法为CompareTo(),此处为Compare()方法)

// 自定义比较方法类 class PeopleComparer : IComparer<People> {   // 区别于CompareTo()单参数,此处为双参数   public int Compare(People x, People y)   {     if (x.Name != y.Name)     {       return x.Name.CompareTo(y.Name);     }     else if (x.Age != y.Age)     {       return x.Age.CompareTo(y.Age);     }     else return 0;   } }   // 客户端 // 传入参数为自定义比较类的实例       peopleList.Sort(new PeopleComparer());   // OUTPUT: //   李四 18 //   王五 16 //   王五 30 //   张三 22 //   张三 24

同理,List<T>.Sort(int index, int count, IComparer<T> Comparer) 方法的参数:待排元素起始索引、待排元素个数、排序方法

方法三、采用泛型委托 Comparison<T>,绑定自定义的比较方法

区别于上述继承接口的方法,此方法的参数为 泛型委托 Comparison<T>

委托原型:public delegate int Comparison<in T>(T x, T y);

方法三:依照委托的使用方法,首先创建委托实例MyComparison,并绑定到自定义的比较方法PeopleComparison()上,最终调用list.Sort()时 将委托实例传入

原理:list.Sort()根据传入的委托方法,进行两两元素比较最终实现排序

// 客户端 class Client {   // 方法0 自定义比较方法   public static int PeopleComparison(People p1, People p2)   {     if (p1.Name != p2.Name)     {       return p1.Name.CompareTo(p2.Name);     }     else if (p1.Age != p2.Age)     {       return p1.Age.CompareTo(p2.Age);     }     else return 0;   }     static void Main(string[] args)   {     / 创建list ... /          // 方法0 创建委托实例并绑定     Comparison<People> MyComparison = PeopleComparison;       // 传入该实例实现比较方法     peopleList.Sort(MyComparison);       // OUTPUT:     //   李四 18     //   王五 16     //   王五 30     //   张三 22     //   张三 24   } }

此外,既然Comparison<T>是泛型委托,则完全可以用 Lambda表达式 进行描述:

// Lambda表达式实现Comparison委托 peopleList.Sort((p1, p2) => {   if (p1.Name != p2.Name)   {     return p2.Name.CompareTo(p1.Name);   }   else if (p1.Age != p2.Age)   {     return p2.Age.CompareTo(p1.Age);   }   else return 0; });   // OUTPUT: //   张三 24 //   张三 22 //   王五 30 //   王五 16 //   李四 18

总结

虽然本文仅使用了List<T>一种容器对Sort()方法进行阐述,但是不同容器的使用Sort()的方法大相径庭,因为核心的原理都是应用两种接口及泛型委托:

两种接口:IComparable<T> 、 IComparer<T>

泛型委托:Comparison<T>

参考

IComparable接口 - Microsoft

Comparison委托 - Microsoft

IComparer接口 - Microsoft

附:一个完整的测试Demo

using System; using System.Collections.Generic; using System.Linq; using System.Text;  namespace ListSort {   class Program   {     static void DisplayInfo<T>(List<T> list) {       //输出List元素内容       foreach(var item in list) {         System.Console.Write("{0} ",item.ToString());       }       System.Console.WriteLine("");     }       // 方法3 自定义委托泛型比较方法     public static int PeopleComparison(People p1, People p2)     {       if (p1.Name != p2.Name)       {         return p1.Name.CompareTo(p2.Name);       }       else if (p1.Age != p2.Age)       {         return p1.Age.CompareTo(p2.Age);       }       else return 0;     }     static void Main(string[] args)     {       List<People> peopleList = new List<People>();       peopleList.Add(new People("张三", 22));       peopleList.Add(new People("张三", 24));       peopleList.Add(new People("李四", 18));       peopleList.Add(new People("王五", 16));       peopleList.Add(new People("王五", 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<T>接口。       peopleList.Sort(new PeopleComparer());         // 方法2 除以上两种方法以外还可以使用另一种方法,在People类中实现IComparable<T>       peopleList.Sort();       System.Console.WriteLine("方法3排序后数据:");       DisplayInfo(peopleList);         // 方法3 创建泛型委托实例并绑定       Comparison<People> MyComparison = PeopleComparison;         // 传入该实例实现比较方法       peopleList.Sort(MyComparison);         System.Console.WriteLine("方法3排序后数据:");       DisplayInfo(peopleList);         // 方法3 使用Comparison<T>委托,Lambda写法       peopleList.Sort((left, right) =>       {         //先按姓名排序,如果姓名相同再按年龄排序         int x = left.Name.CompareTo(right.Name);         if(x==0) {           if (left.Age > right.Age)             x = 1;           else if (left.Age == right.Age)             x = 0;           else             x = -1;         }         return x;       });      }   }     //方法一   public class People : IComparable<People>   {     public int Age { get;set;}     public string Name { get;set;}     public People(string name,int age) {       this.Name = name;       this.Age = age;     }       public override string ToString() {       string result = "";       result = "["+this.Name+","+ this.Age.ToString()+"]";       return result;      }       public int CompareTo(People other)     {       int x = this.Name.CompareTo(other.Name);       if(x==0) {         if (this.Age > other.Age)           x = 1;         else if (this.Age == other.Age)           x = 0;         else           x = -1;       }       return x;     }   }     //方法二   public class PeopleComparer : IComparer<People>   {     public int Compare(People left, People right)     {       int x = left.Name.CompareTo(right.Name);       if(x==0) {         if (left.Age > right.Age)           x = 1;         else if (left.Age == right.Age)           x = 0;         else           x = -1;       }       return x;     }   }  }

补充:C# IComparable和IComparer接口和自定义比较器

前言

ArrayList里面有一个方法:

public virtual void Sort(IComparer comparer);

使用指定的比较器对整个 System.Collections.ArrayList 中的元素进行排序。

comparer:比较元素时要使用的 System.Collections.IComparer 实现。

啥玩意啊?

正文

1.Comparer类简单介绍

想弄清楚这个,我们先来看看这么一个类。

在System.Collections名称空间中,有这么一个类:Comparer。顾名思义,他可以实现对简单类型的比较,什么意思呢?来看如下代码:

int a=1,b=2;

正常情况下,我们要怎样比较他们的大小?if,运算符,……?这当然可以,不过Comparer已经给我们提供了一个函数,可以直接使用:(需要using System.Collections;)

Console.WriteLine(Comparer.Default.Compare(a,b));

因为a<b,所以控制台会输出-1。(这个函数总是返回-1,0,1三个值。)

这里通过Comparer里的静态属性Default获得Comparer的实例调用了Comparer里的非静态函数Compare。

(还可以比较根据字母比较两个string类型,这里就省略介绍了)

2.自定义比较器,IComparable,IComparer接口

当然,这个类不仅仅只是用来比较两个数的大小的。有时候我们想直接比较两个对象,但是引用里面的属性或许比较麻烦。尤其是参考要素过多,不好直接比较的时候,怎样才能更高效地比较两个对象呢?这时候,我们就需要自定义比较器了。

首先来介绍IComparable接口。这个接口里只有一个方法CompareTo()。让你的类实现这个接口的CompareTo方法,就可以直接调用这个方法和另一个对象比较。下面是例子:

public class ClassTest : IComparable {  public int intTest;  public int CompareTo(object obj)  {   return intTest-((ClassTest)obj).intTest;   //这里的代码可以按需要自己编写,这里只是一个简单的示例  } }

然后就可以直接使用啦:

ClassTest a = new ClassTest(){intTest=1}; ClassTest b = new ClassTest(){intTest=2}; Console.WriteLine(a.CompareTo(b));//输出-1 Comparer类已经为我们提供了IComparer的默认实现,但我们仍然可以自定义它。新建一个类:(记得using System.Collections;) public class ClassTestComparer : IComparer {  public static IComparer Default = new ClassTestComparer();  //这里必须使用这样的定义,将对象转化为IComparer类型有很大用处,下面会介绍  public int Compare(object a,object b)  {   return ((ClassTest)a).intTest - ((ClassTest)b).intTest;   //同样这里使用最简单的示例,但是你可以大放异彩  } }

注意,如果用于比较的类和设定的类不一样,就会出现错误。

使用示例:

ClassTest a = new ClassTest(){intTest=1}; ClassTest b = new ClassTest(){intTest=2}; Console.WriteLine(ClassTestComparer.Default.Compare(a,b)); //结果是-1

可以发现,这两个接口的不同之处在于:IComparable在要比较的对象的类中实现,可以比较该对象和另一个对象。IComparer在一个单独的类中实现,可以比较任意两个对象(关键是你的设置)。

3.对集合排序

当然,这两个接口还有更强大的用处。我们可以使用这两个接口对集合进行排序。还记得前言里的Sort()方法吗?接下来就以ArrayList为例,介绍如何使用。

ArrayList ClassTests = new ArrayList(); ClassTest a = new ClassTest(){intTest=1}; ClassTest b = new ClassTest(){intTest=2}; ClassTest c = new ClassTest(){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,否则会报错。

一般来说,都是对同一个类进行比较。不过,也可以实现对不同类比较的代码,这就看具体需要了。

看完上述内容是否对您有帮助呢?如果还想对相关知识有进一步的了解或阅读更多相关文章,请关注亿速云行业资讯频道,感谢您对亿速云的支持。

向AI问一下细节

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

AI