草庐IT

C# List.Sort排序

Peter_Gao_ 2023-04-05 原文

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

C#  默认排序方法Sort、Reverse

排序Sort,倒序Reverse

//默认是元素第一个字母按升序
list.Sort();
//将List里面元素顺序反转
list.Reverse();
//从第二个元素开始,反转4个元素
//结果list里最后的顺序变成"Ha", "Jay", "Lily", "Tom", "Hunter", "Jim", "Kuku",  "Locu"
list.Reverse(1,4);

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));
    }
}

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

对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

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

增加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
    }
}

泛型委托用 Lambda表达式

此外,既然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 IComparable
Comparison委托 - Microsoft Comparison
IComparer接口 - Microsoft IComparer 接口 (System.Collections) | Microsoft Docs
IComparable和IComparer接口和自定义比较器 - My_Pure C# IComparable和IComparer接口和自定义比较器 - 简书


  附:一个完整的测试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# List.Sort排序的更多相关文章

  1. ruby - Sort_by Ruby,一个降序,一个升序 - 2

    我已经搜索过这个问题的答案,但没有成功,有一个类似的问题,但答案在这种情况下不起作用,它按数字项目排序。SimilarQuestion-Thatdidnotwork我正在尝试使用ruby​​的sort_by对一个项目进行降序排序和另一个升序排序。我只能找到一个。代码如下:#PrimarysortLastNameDescending,withtiesbrokenbysortingAreaofinterest.people=people.sort_by{|a|[a.last_name,a.area_interest]}任何指导肯定会有所帮助。示例数据:输入罗素,逻辑欧拉,图论伽罗瓦,抽象代

  2. ruby-on-rails - 需要帮助最大化多个相似对象中的 3 个因素并适当排序 - 2

    我需要用任何语言编写一个算法,根据3个因素对数组进行排序。我以度假村为例(如Hipmunk)。假设我想去度假。我想要最便宜的地方、最好的评论和最多的景点。但是,显然我找不到在所有3个中都排名第一的方法。Example(assumingthereare20importantattractions):ResortA:$150/night...98/100infavorablereviews...18of20attractionsResortB:$99/night...85/100infavorablereviews...12of20attractionsResortC:$120/night

  3. ruby-on-rails - 在具有 ActiveRecord 条件的相关模型中按字段排序 - 2

    我正在尝试按Rails相关模型中的字段进行排序。我研究的所有解决方案都没有解决如果相关模型被另一个参数过滤?元素模型classItem相关模型:classPriority我正在使用where子句检索项目:@items=Item.where('company_id=?andapproved=?',@company.id,true).all我需要按相关表格中的“位置”列进行排序。问题在于,在优先级模型中,一个项目可能会被多家公司列出。因此,这些职位取决于他们拥有的company_id。当我显示项目时,它是针对一个公司的,按公司内的职位排序。完成此任务的正确方法是什么?感谢您的帮助。PS-我

  4. ruby - 按数字(从大到大)然后按字母(字母顺序)对对象集合进行排序 - 2

    我正在构建一个小部件来显示奥运会的奖牌数。我有一个“国家”对象的集合,其中每个对象都有一个“名称”属性,以及奖牌计数的“金”、“银”、“铜”。列表应该排序:1.首先是奖牌总数2.如果奖牌相同,按类型分割(金>银>铜,即2金>1金+1银)3.如果奖牌和类型相同,则按字母顺序子排序我正在用ruby​​做这件事,但我想语言并不重要。我确实找到了一个解决方案,但如果感觉必须有更优雅的方法来实现它。这是我做的:使用加权奖牌总数创建一个虚拟属性。因此,如果他们有2个金牌和1个银牌,加权总数将为“3.020100”。1金1银1铜为“3.010101”由于我们希望将奖牌数排序为最高的,因此列表按降序排

  5. ruby-on-rails - 在不重新查询数据库的情况下重新排序 Rails 中的事件记录? - 2

    例如,假设我有一个名为Products的模型,并且在ProductsController中,我有以下代码用于product_listView以显示已排序的产品。@products=Product.order(params[:order_by])让我们想象一下,在product_listView中,用户可以使用下拉菜单按价格、评级、重量等进行排序。数据库中的产品不会经常更改。我很难理解的是,每次用户选择新的order_by过滤器时,rails是否必须查询,或者rails是否能够以某种方式缓存事件记录以在服务器端重新排序?有没有一种方法可以编写它,以便在用户排序时rails不会重新查询结果

  6. ruby-on-rails - Rails - Carrierwave 进程抛出 ArgumentError : no images in this image list - 2

    在尝试实现应用auto_orient的过程之后!对于我的图片,我收到此错误:ArgumentError(noimagesinthisimagelist):app/uploaders/image_uploader.rb:36:in`fix_exif_rotation'app/controllers/posts_controller.rb:12:in`create'Carrierwave在没有进程的情况下工作正常,但在添加进程后尝试上传图像时抛出错误。流程如下:process:fix_exif_rotationdeffix_exif_rotationmanipulate!do|image|

  7. ruby-on-rails - 如何对对象数组进行排序? - 2

    我有一个对象如下:[{:id=>2,:fname=>"Ron",:lname=>"XXXXX",:photo=>"XXX"},{:id=>3,:fname=>"Dain",:lname=>"XXXX",:photo=>"XXXXXXX"},{:id=>1,:fname=>"Bob",:lname=>"XXXXXX",:photo=>"XXXX"}]我想按fname排序,不区分大小写,所以它会导致编号:1,3,2我该如何排序?我正在尝试:@people.sort!{|x,y|y[:fname]x[:fname]}但这没有任何效果。 最佳答案

  8. ruby - 使用自定义排序首选项对数组进行排序? - 2

    有人可以告诉我如何根据自定义字符串对嵌套数组进行排序吗?比如有没有办法排序:[['Red','Blue'],['Green','Orange'],['Purple','Yellow']]“橙色”、“黄色”,然后是“蓝色”?最终结果如下所示:[['Green','Orange'],['Purple','Yellow'],['Red','Blue']]它不是按字母顺序排序的。我很想知道我是否可以定义要排序的值以实现上述目标。 最佳答案 sort_by对于这种排序总是非常方便:a=[['Red','Blue'],['Green','Ora

  9. Ruby 将对象插入现有的已排序对象数组 - 2

    我有以下现有的Dog对象数组,它们按age属性排序:classDogattr_accessor:agedefinitialize(age)@age=ageendenddogs=[Dog.new(1),Dog.new(4),Dog.new(10)]我现在想插入一条新的狗记录,并将它放在数组中的正确位置。假设我想插入这个对象:another_dog=Dog.new(8)我想把它插入到数组中,让它成为数组中的第三项。这是一个人为的示例,旨在演示我特别想如何将一个项目插入到现有的有序数组中。我意识到我可以创建一个全新的数组并重新对所有对象进行排序,但这不是我的目标。谢谢!

  10. ruby - 如何排序不是简单的哈希(哈希的哈希) - 2

    我有一个这样的哈希{55=>{:value=>61,:rating=>-147},89=>{:value=>72,:rating=>-175},78=>{:value=>64,:rating=>-155},84=>{:value=>90,:rating=>-220},95=>{:value=>39,:rating=>-92},46=>{:value=>97,:rating=>-237},52=>{:value=>73,:rating=>-177},64=>{:value=>69,:rating=>-167},86=>{:value=>68,:rating=>-165},53=>{:va

随机推荐