C#中List.Sort()--集合排序方法分析

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

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

  1. List.Sort();         
  2. List.Sort(IComparer Comparer);
  3. List.Sort(int index, int count, IComparer Comparer);
  4. List.Sort(Comparison 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 peopleList = new List();
        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:定义由值类型或类实现的通用比较方法,旨在创建特定于类型的比较方法以对实例进行排序。
  • 原理:自行实现的CompareTo()方法会在list.Sort()内部进行元素两两比较,最终实现排序
class People : IComparable
{
    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
{
    // 区别于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.Sort(int index, int count, IComparer Comparer) 方法的参数:待排元素起始索引、待排元素个数、排序方法

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

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

  • 委托原型:public delegate int Comparison(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 MyComparison = PeopleComparison;

        // 传入该实例实现比较方法
        peopleList.Sort(MyComparison);

        // OUTPUT:
        //      李四 18
        //      王五 16
        //      王五 30
        //      张三 22
        //      张三 24
    }
}

此外,既然Comparison是泛型委托,则完全可以用 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一种容器对Sort()方法进行阐述,但是不同容器的使用Sort()的方法大相径庭,因为核心的原理都是应用两种接口及泛型委托

  • 两种接口IComparable 、 IComparer
  • 泛型委托Comparison

参考

  • IComparable接口 - Microsoft
  • Comparison委托 - Microsoft
  • IComparer接口 - Microsoft
  • IComparable和IComparer接口和自定义比较器 - My_Pure

 附:一个完整的测试Demo

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
  
namespace ListSort
{
    class Program
    {
        static void DisplayInfo(List 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 peopleList = new List();
            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接口。
            peopleList.Sort(new PeopleComparer());
  
            // 方法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) =>
            {
                //先按姓名排序,如果姓名相同再按年龄排序
                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
    {
        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
   {
       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#,C#教学)