自定义List.Sort排序方式,以实现多参数,多规则的复杂排序
默认排序方法
1 2 3 4 5 6
| List.Sort();
List.Reverse();
List.Reverse(1,4);
|
自定义排序的四种方法
1 2 3 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参数进行排序,排序规则为,先按姓名升序排序,如果姓名相同再按年龄的升序排序。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| 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)); } }
|
方法一:继承IComparable()接口,实现Compareto()方法
对People类继承IComparable接口,实现CompareTo()方法
该方法为系统默认的方法,单一参数时会默认进行升序排序。但遇到多参数(Name、Age)排序时,我们需要对该默认方法进行修改。
IComparable<T>:定义由值类型或类实现的通用比较方法,旨在创建特定于类型的比较方法以对实例进行排序。
原理:自行实现的CompareTo()方法会在list.Sort()内部进行元素两两比较,最终实现排序
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| class People : IComparable<People> { public People(string name, int age) { Name = name;Age = age; } public string Name { get; set; } public int Age { get; set; } 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();
|
方法二:增加外部比较类,继承IComparer接口,实现Compare()方法
增加People类的外部比较类,继承IComparer接口、实现Compare()方法
区别于上述继承IComparable的方法,该方法不可在People内继承实现IComparer接口,而是需要新建比较方法类进行接口实现
新建PeopleComparer类、继承IComparer接口、实现Compare()方法
原理:list.Sort()将PeopleComparer类的实例作为参数,在内部使用Compare()方法进行两两比较,最终实现排序(注:上述方法为CompareTo(),此处为Compare()方法)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| class PeopleComparer : IComparer<People> { 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());
|
同理,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()根据传入的委托方法,进行两两元素比较最终实现排序
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| class Client { 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 ... / Comparison<People> MyComparison = PeopleComparison; peopleList.Sort(MyComparison); } }
|
泛型委托用 Lambda表达式
此外,既然Comparison<T>是泛型委托,则完全可以用 Lambda表达式 进行描述:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| 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; });
|
总结
虽然本文仅使用了List<T>一种容器对Sort()方法进行阐述,但是不同容器的使用Sort()的方法大相径庭,因为核心的原理都是应用两种接口及泛型委托:
两种接口:IComparable<T> 、 IComparer<T>
泛型委托:Comparison<T>
参考
IComparable接口 - IComparable Interface (System) | Microsoft Docs
Comparison委托 - Comparison Delegate (System) | Microsoft Docs
IComparer接口 - IComparer Interface (System.Collections) | Microsoft Docs
IComparable和IComparer接口和自定义比较器 - https://www.jianshu.com/p/2f2dd4829842
Unity中应用方式
新建数据类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| [System.Serializable] public struct TestDataForSort { public int id; public string description; public Quality quality; public TestDataForSort(int id,string description,Quality quality) { this.id = id; this.description = description; this.quality = quality; } } public enum Quality { common, uncommon, rare, epic, legendary }
|
新建脚本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
| using System.Collections; using System.Collections.Generic; using UnityEngine;
public class ScriptForSort : MonoBehaviour { [SerializeField] private List<TestDataForSort> testDataForSorts = new List<TestDataForSort>(); void Start() { testDataForSorts.Add(new TestDataForSort(0, "Mof", Quality.common)); testDataForSorts.Add(new TestDataForSort(2, "Bao", Quality.rare)); testDataForSorts.Add(new TestDataForSort(1, "Cur", Quality.legendary)); }
void OnGUI() { if (GUILayout.Button("SortByID")) { testDataForSorts.Sort((t1, t2) => { return t1.id.CompareTo(t2.id); }); } if (GUILayout.Button("SortByName")) { testDataForSorts.Sort((t1, t2) => { return t1.description.CompareTo(t2.description); }); } if (GUILayout.Button("SortByQuality")) { testDataForSorts.Sort((t1, t2) => { return t1.quality.CompareTo(t2.quality); }); } } }
|