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

在C#中,List.Sort() 不僅爲我們提供了默認的排序方法,還爲我們提供了4種自定義排序的方法,通過默認排序方法,我們無需重寫任何Sort()方法的實現代碼,就能對單參數類型的List數據進行單一規則的排序,如果通過對這些方法進行改進我們可以輕鬆做到對多參數、多規則的複雜排序。

下面是C#自定義排序的4種方法:

  1. List<T>.Sort();         
  2. List<T>.Sort(IComparer<T> Comparer);
  3. List<T>.Sort(int index, int count, IComparer<T> Comparer);
  4. 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>

參考

 附:一個完整的測試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;
       }
   }

}   

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章