C# 泛型集合中的排序

news2024/9/23 5:16:09
  1. 集合排序问题与ICompareble

对于C#最常见的集合List<T>,有时候需要进行排序,而List是直接有Sort方法,因此对于一个简单地整数集合排序,很简单:

List<int> ints = new List<int>() { 1, 3, 1, 4, 5, 9, 8 };
ints.ForEach(i => Console.Write(i+" - "));
ints.Sort();
Console.WriteLine();
ints.ForEach(i => Console.Write(i + " - "));
1 - 3 - 1 - 4 - 5 - 9 - 8 -
1 - 1 - 3 - 4 - 5 - 8 - 9 -

如果集合中是自定义数据,如下面的雇员类:

    internal class Employee
    {
        public int ID { get; set; }
        public string Name { get; set; }=String.Empty;
        public string Gender { get; set; }=String.Empty ;
        public int Salary { get; set; }
    }

那么直接调用Sort则会出现问题,举例如下:

List<Employee> listEmployees = new List<Employee>
            {
                new Employee() { ID = 101, Name = "Pranaya", Gender = "Male", Salary = 5000 },
                new Employee() { ID = 102, Name = "Priyanka", Gender = "Female", Salary = 7000 },
                new Employee() { ID = 103, Name = "Anurag", Gender = "Male", Salary = 5500 },
                new Employee() { ID = 104, Name = "Sambit", Gender = "Male", Salary = 6500 },
                new Employee() { ID = 105, Name = "Hina", Gender = "Female", Salary = 6500 }
            };
Console.WriteLine("Employees Before Sorting");
foreach (Employee employee in listEmployees)
{
    Console.WriteLine("ID = {0}, Name = {1},  Gender = {2}, Salary = {3}",
        employee.ID, employee.Name, employee.Gender, employee.Salary);
}
listEmployees.Sort();
Console.WriteLine("\nEmployees After Sorting");
foreach (Employee employee in listEmployees)
{
    Console.WriteLine("ID = {0}, Name = {1},  Gender = {2}, Salary = {3}",
        employee.ID, employee.Name, employee.Gender, employee.Salary);
}

运行时会报错:

出错的原因很简单,Sort方法不知道如何对Employee对象排序。那为什之前对List<int> 类型可以排序呢?

因为像int, double, string, decimal, char,这些基本类型都已经实现了IComparable接口,看一下int类型的定义:

所以我们要实现对Employee集合的排序,也应该让Employee实现这个接口。

添加接口ICompareble<T>之后,会提示要求实现CompareTo方法:

CompareTo方法有一个参数,跟自己的类型一样,返回一个Int类型ans,分别用正,0,负代表要比较的对象的关系:

  1. ans>0:代表当前对象大于传入的对象

  1. ans==0:代表两个对象相等

  1. ans<0:代表当前对象小于传入对象

实现如下:

        public int CompareTo(Employee? obj)
        {
            if (obj == null)
                return 1;
            if (this.Salary > obj.Salary)
            {
                return 1;
            }
            else if (this.Salary < obj.Salary)
            {
                return -1;
            }
            else
            {
                return 0;
            }
        }

运行结果:

Employees Before Sorting
ID = 101, Name = Pranaya,  Gender = Male, Salary = 5000
ID = 102, Name = Priyanka,  Gender = Female, Salary = 7000
ID = 103, Name = Anurag,  Gender = Male, Salary = 5500
ID = 104, Name = Sambit,  Gender = Male, Salary = 6500
ID = 105, Name = Hina,  Gender = Female, Salary = 6500

Employees After Sorting
ID = 101, Name = Pranaya,  Gender = Male, Salary = 5000
ID = 103, Name = Anurag,  Gender = Male, Salary = 5500
ID = 104, Name = Sambit,  Gender = Male, Salary = 6500
ID = 105, Name = Hina,  Gender = Female, Salary = 6500
ID = 102, Name = Priyanka,  Gender = Female, Salary = 7000

考虑到工资是一个Int类型,而此类型本身也实现了Comparable接口,因为可以简化比较函数,直接用改为:

        public int CompareTo(Employee? obj)
        {
            if (obj == null)
                return 1;
            //if (this.Salary > obj.Salary)
            //{
            //    return 1;
            //}
            //else if (this.Salary < obj.Salary)
            //{
            //    return -1;
            //}
            //else
            //{
            //    return 0;
            //}
            return Salary.CompareTo(obj.Salary);
        }

到此为止,好像问题都解决了,但试想一下,我们实现的接口是按工资来排序,假设有时候要按照人名排序,有时候按性别排序咋办呢?一个接口只有一个实现,所以上面的方法不够灵活。

  1. 比较器类

List类的Sort方法有4个重载,其中一个是Sort(IComparer<T> comparer),也就是你可以直接传入一个实现ICompare<T>的对象,Sort方法会调用次对象的固定方法,这里我们将这个特殊的对象叫做比较器。

回到上面的问题,我们来写一个比较器实现用名字比较:

    public class SortByName : IComparer<Employee>
    {
        int IComparer<Employee>.Compare(Employee? x, Employee? y)
        {
            return x!.Name.CompareTo(y!.Name);
        }
    }

在原来的代码后面再加上如下几行:

Console.WriteLine("\n Employees After Sorting by Name");
listEmployees.Sort(new SortByName());
foreach (Employee employee in listEmployees)
{
    Console.WriteLine("ID = {0}, Name = {1},  Gender = {2}, Salary = {3}",
        employee.ID, employee.Name, employee.Gender, employee.Salary);
}

运行即可看到列表按姓名字母排序了。

这样就将比较函数和原生的Employee类解耦,后面想安性别排序,也可以再实现一个类似的比较器。

  1. 使用Linq排序

熟悉linq的朋友都知道,前面将的两种排序方法有点繁琐(过时),很多复杂的集合操作都可以用linq轻松搞定。就算不熟悉Linq,熟悉SQL查询语句的,也能很快上手Linq。这里我就不介绍Linq基础知识了,大家可以看看我之前写的Linq文章。

这里简单展示一下Linq的魅力:

Console.WriteLine("\n Use Linq Sort");
var ans=listEmployees.OrderBy(x => x.ID).ToList();
ans.ForEach(employee => Console.WriteLine("ID = {0}, Name = {1},  Gender = {2}, Salary = {3}",
        employee.ID, employee.Name, employee.Gender, employee.Salary));

Console.WriteLine("We got a new Copy");
listEmployees.ForEach(employee => Console.WriteLine("ID = {0}, Name = {1},  Gender = {2}, Salary = {3}",
        employee.ID, employee.Name, employee.Gender, employee.Salary));

顺便要说的是,Linq返回的是一个新的List,不会影响排序前的List,这和Sort方法不一样。

  1. 使用代理Comparison<T>排序

Sort的另一个重载是:Sort(Comparison<T> comparison): 其中Comparison是一个delegate类型:

 // 摘要:
    //     Represents the method that compares two objects of the same type.
    //
    // 参数:
    //   x:
    //     The first object to compare.
    //
    //   y:
    //     The second object to compare.
    //
    // 类型参数:
    //   T:
    //     The type of the objects to compare.
    //
    // 返回结果:
    //     A signed integer that indicates the relative values of x and y, as shown in the
    //     following table.
    //     Value – Meaning
    //     Less than 0 –x is less than y.
    //     0 –x equals y.
    //     Greater than 0 –x is greater than y.
public delegate int Comparison<in T>(T x, T y);

有了前面的介绍,我也不多说了,直接看代码:

listEmployees.Sort(CompareEmployees);


static int CompareEmployees(Employee e1,Employee e2)
{
    return e1.Salary.CompareTo(e2.Salary);
}

所有用代理的地方基本上也可以用Lambda表达式:所以上面几行可以用下面一行代替:

listEmployees.Sort((e1, e2) => e1.ID.CompareTo(e2.ID));

显而易见,Lmbda表达式更简洁优雅,所以Delegate也过时了。


  1. 小结

从前面的讨论可以看出最简单的排序方法是利用Linq或者Lambda,另外两种会增加额外的代码量,但是我们应该了解其背后的原理,Linq和Lambda都是C#后面陆续推出的,自然会更能节省开发者时间。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/342875.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

LeetCode-222. 完全二叉树的节点个数

目录递归法改进题目来源222. 完全二叉树的节点个数递归法 递归三步曲 1.确定递归函数的参数和返回值 参数就是传入树的根节点&#xff0c;返回就返回以该节点为根节点二叉树的节点数量&#xff0c;所以返回值为int类型。 int countNodes(TreeNode root)2.确定终止条件 如果…

网络安全-Pyhton环境搭建

网络安全-Pyhton环境搭建 https://www.kali.org/get-kali/#kali-installer-images—kali官网下载地址 python这个东东呢 是目前来说最简单&#xff0c;方便的开源的脚本语言 广泛用于Web开发&#xff0c;AI&#xff0c;网站开发等领域 python要装2和3 为什么要安装两个版本…

Word处理控件Aspose.Words功能演示:使用 C++ 合并 MS Word 文档

Aspose.Words 是一种高级Word文档处理API&#xff0c;用于执行各种文档管理和操作任务。API支持生成&#xff0c;修改&#xff0c;转换&#xff0c;呈现和打印文档&#xff0c;而无需在跨平台应用程序中直接使用Microsoft Word。此外&#xff0c;API支持所有流行的Word处理文件…

34岁测试工程师被辞退,难道测试岗位真的只是青春饭吗?

一&#xff1a;前言&#xff1a;人生的十字路口静坐反思 入软件测试这一行至今已经10年多&#xff0c;承蒙领导们的照顾与重用&#xff0c;同事的支持与信任&#xff0c;我的职业发展算是相对较好&#xff0c;从入行到各类测试技术岗位&#xff0c;再到测试总监&#xff0c;再…

阿里云共享标准型S6实例云服务器特性、适用场景及性能评测

阿里云共享标准型s6实例是相对于上一代共享型xn4、n4、mn4和e4实例&#xff0c;在性能上更加更定的全新一代共享型实例。相比与突发性能型T5,T6实例&#xff0c;S6是不限制CPU性能&#xff0c;非常适合个人网站和企业展示性网站等中小型网站和Web应用程序。相对于阿里云当下其他…

基于微信云开发的防诈反诈宣传教育答题小程序

基于微信云开发的防诈反诈宣传教育答题小程序一、前言介绍作为当代大学生&#xff0c;诈骗事件的发生屡见不鲜&#xff0c;但却未能引起大家的重视。高校以线上宣传、阵地展示为主&#xff0c;线下学习、实地送法为辅&#xff0c;从而构筑立体化反诈骗防线。在线答题考试是一种…

【gcc/g++】程序的翻译(.c -->.exe)

环境&#xff1a;centos7.6&#xff0c;腾讯云服务器Linux文章都放在了专栏&#xff1a;【Linux】欢迎支持订阅&#x1f339;前言我们在写完代码运行时会发现生成了一个.exe的可执行程序&#xff0c;那么该程序是如何形成的呢&#xff1f;本次章节将在linux下用编译器gcc进行一…

2019年广东工业大学腾讯杯新生程序设计竞赛(同步赛)

同步赛链接 A-原初的信纸(最值&#xff0c;STL&#xff09; 题意&#xff1a; 找 n 个数的最大值. 参考代码&#xff1a; void solve() {int n;std::cin >> n;std::vector<int> a(n);for (auto &c : a)std::cin >> c;std::cout << *max_element…

3. SpringMVC Rest 风格

1. REST 简介 REST&#xff08;Representational State Transfer&#xff09;&#xff0c;表现形式状态转换&#xff0c;它是一种软件架构风格。 当要表示一个网络资源的时候&#xff0c;可以使用两种方式&#xff1a; 传统风格资源描述形式 http://localhost/user/getById?…

ts快速上手之实现hello world 常见的踩坑排除

前言 大家新年快乐&#xff0c;刚过了一个年又快要到情人节了&#xff0c;提前祝各位同行有女朋友的情人节快乐&#xff0c;没有对象那就new一个对象吧[滑稽] 关于TypeScript 闲话少说&#xff0c;相信大家在这个平台上看到的文章好多开头都会说一些介绍是多久诞生的&#x…

Java面试——MyBatis篇

✅作者简介&#xff1a;2022年博客新星 第八。热爱国学的Java后端开发者&#xff0c;修心和技术同步精进。 &#x1f34e;个人主页&#xff1a;Java Fans的博客 &#x1f34a;个人信条&#xff1a;不迁怒&#xff0c;不贰过。小知识&#xff0c;大智慧。 &#x1f49e;当前专栏…

Flutter(一)介绍、Dart语言简介

Flutter介绍 纯原生开发主要面临动态化更新和开发成本两个问题&#xff0c;而针对这两个问题&#xff0c;诞生了一些跨平台的动态化框架 跨平台技术简介 Flutter 是 Google 推出并开源的移动应用开发框架&#xff0c;主打跨平台、高保真、高性能。开发者可以通过 Dart 语言开…

【云原生kubernetes】k8s service使用详解

一、什么是服务service&#xff1f; 在k8s里面&#xff0c;每个Pod都会被分配一个单独的IP地址,但这个IP地址会随着Pod的销毁而消失&#xff0c;重启pod的ip地址会发生变化&#xff0c;此时客户如果访问原先的ip地址则会报错 &#xff1b; Service (服务)就是用来解决这个问题的…

Django设计模式以及模板层介绍

MVC和MTV 传统的MVC作用&#xff1a;降低模块间的耦合度&#xff08;解耦&#xff09;Django的MTV模式 作用&#xff1a;降低模块间的耦合度&#xff08;解耦&#xff09;什么是模板 1、模板是可以根据字典数据动态变化的html网页2、模板可以根据视图中传递的字典数据动态生成相…

AtCoder Grand Contest 061(题解)

A - Long Shuffle 这道题本质是一个找规律的题 既然是打表题&#xff0c;我们先暴力把他打出来 (盗一张图.jpg) 接下来就是在这张图中挖掘答案 我们可以明显的看到偶数行是有一些规律的 要么是相邻对的互换&#xff0c;要么不变 不变和互换的位置也有讲究&#xff0c;在二进制…

DS期末复习卷(四)

一、选择题(每题1分共 20分) 1&#xff0e;设一维数组中有n个数组元素&#xff0c;则读取第i个数组元素的平均时间复杂度为&#xff08;C &#xff09;。 (A) O(n) (B) O(nlog2n) © O(1) (D) O(n2) 数组读取元素的时间复杂度为O&#xff08;1&#xff09; 2&#xff0e;设…

SpringCloud Alibaba

文章目录&#x1f68f; 第十七章 SpringCloud Alibaba入门简介&#x1f6ac; 一、为什么使用Alibaba&#x1f6ad; 1、spring netflix进入维护模式&#x1f6ad; Spring cloud alibaba&#x1f6ac; 二、如何使用&#xff1f;&#x1f6ac; 三、版本对应&#x1f68f; 第十八章…

Jmeter接口测试+压力测试

jmeter是apache公司基于java开发的一款开源压力测试工具&#xff0c;体积小&#xff0c;功能全&#xff0c;使用方便&#xff0c;是一个比较轻量级的测试工具&#xff0c;使用起来非常简单。因为jmeter是java开发的&#xff0c;所以运行的时候必须先要安装jdk才可以。jmeter是免…

朴素贝叶斯算法和拉普拉斯平滑详细介绍及其原理详解

相关文章 K近邻算法和KD树详细介绍及其原理详解朴素贝叶斯算法和拉普拉斯平滑详细介绍及其原理详解 文章目录相关文章前言一、朴素贝叶斯算法二、拉普拉斯平滑总结前言 今天给大家带来的主要内容包括&#xff1a;朴素贝叶斯算法、拉普拉斯平滑。这些内容也是机器学习的基础内容…

AXI-Lite 学习笔记

AXI-Lite 学习笔记 参考 FPGA&#xff1a;AXI_Lite总线基础2-1]、第二节 AXI总线介绍、ZYNQ PL与PS交互专题_哔哩哔哩_bilibili AXI-Lite总线系列1 - 基础知识_哔哩哔哩_bilibili AXI4 介绍 AXI4 是ARM公司提出的一种片内总线&#xff0c;描述了主从设备之间的数据传输方式。主…