C#排序比较

与C#定义了相等性比较规范一样,C#也定义了排序比较规范,以确定一个对象与另一个对象的先后顺序。排序规范如下

  • IComparable接口(包括IComparable接口和IComparable<T>接口)
  • >和<运算符

当需要实现排序算法时,使用IComparable接口。在下面的例子中,Array.Sort静态方法可以调用,是因为System.String类实现了IComparable接口。

string[] colors={"Green", "Red", "Blue"};
Array.Sort(colors)
foreach(string c in colors)
    Console.Write(c+ " ");

而<和>运算符比较特殊,因为他们一般用于比较数字类型。因为大于和小于运算符会被静态地解析,因此它们“产生”出高效的代码,适用于复杂计算的场景。

.NET Framework还提供了插件式的排序协议--IComparer接口。IComparable接口与IComparer接口的差别类似与IEquatable和IEqualityComparer接口 (关于IEqutable接口和IEqualityComparer接口,请参考C#相等性:http://www.cnblogs.com/yang_sy/p/3582946.html)

1. IComparable接口

IComparable接口的定义如下

public interface IComparable

    int CompareTo(Object obj);
}

public interface IComparable<in T>
{
    int CompareTo(T other);
}

这两个接口定义了相同的功能。对于值类型,IComparable<T>接口效率高于ICompare接口。上面的两个接口的CompareTo方法都按照下面的方式运行:

  • 如果a排在b后面,那么a.CompareTo(b)返回1
  • 如果a和不一样,那么返回0
  • 如果a排在不前面,那么返回-1

我们来看下面的示例代码:

IList<Staff> staffs = new List<Staff>
{
    new Staff{FirstName="AAA", Title="Manager", Dept="Sale"},
    new Staff{FirstName="BBB", Title="Accountant", Dept="Finance"},
    new Staff{FirstName="CCC", Title="Accountant", Dept="Finance"},
};

Console.WriteLine("BBB".CompareTo(staffs[0].FirstName)); // 1
Console.WriteLine("BBB".CompareTo(staffs[1].FirstName)); // 0
Console.WriteLine("BBB".CompareTo(staffs[2].FirstName)); // -1

C#的大部分基本类型都实现了IComparable接口和IComparable<T>接口。很多自定义类型同样也实现了该接口,这样便于排序。

IComarable与Equals

假设一个类型重写了Equals方法并实现了IComparable接口。那么你肯定希望当Equals返回true时,CompareTo应当返回0。而Equals返回false时,CompareTo可以返回任何值。

换句话说,相等性比对比性更严格;反之则不会。因此,当CompareTo说“两个对象相等”时,Equals会说“这两个对象不一定相等”。一个很好的例子来自System.String类。String.Equals方法和==运算符使用序号排序规则比较字符串--也就是通过每个字符的Unicode的值进行排序。而String.CompareTo方法,却使用不那么严格的基于文化区域(culture-dependent)进行比较。对于大多数计算机,字符ǖ和?,Equals返回False,而CompareTo返回0

你可以实现通过IComparer接口,从而完成特定的排序算法。自定义IComparer接口的实现,进一步加大了CompareTo和Equals方法之间的差异。比如不区分大小写的字符串比较器,对于A和a,将返回0. 这也从反面印证了,ComparTo方法不如Equals方法严格。

2. <和>运算符

一些类型,定义了<和>运算符,比如:

bool after2010 = DateTime.Now > new DateTime(2010, 1, 1);
Console.WriteLine(after2010);

当实现<和>运算符之后,你需要保证<和>运算符与IComparable接口保持一致。这也是.NET Framework的标准。

同样地,当一个类型重载了<和>运算符,那么也要求实现IComparable接口,而反之则不需要。实际上,大多数.NET类型实现了IComparable接口,并没有重载<和>运算符。这(排序比较)与相等性比较不一样:

  • 在实现相等性比较时,如果重载了Equals方法,那么一般都重载==运算符
  • 而在实现排序性比较时,如果实现了CompareTo方法,一般不要求重载<运算符和>运算符

一般地,只有在下面的情形中,才需要重载<运算符和>运算符:

  • 一个类型本身包含大于和小于这样的概念
  • 执行先后顺序比较的方式是唯一的
  • 结果不会随文化区域(Cultures)变化而变化

System.Stirng类型不满足最后一条,因此string不支持>操作和<操作。因此 “beck” > “Anne”,编译时会抛出错误。

3. 实现IComparable接口

下面的实例代码中,结构Note表示一个音乐的注释,它实现了IComparable接口,还重载了<运算符和>运算符。为了实例的完整性,我们还重写了Equals和GetHashCode方法,以及重载了==和!=运算符,通过这个例子,你可以全面的了解排序比较。

internal struct Note : IComparable, IComparable<Note>, IEquatable<Note>
{

    private int semitonesFromA;
    public int SemitonesFromA
    {
        get { return semitonesFromA; }
    }

    public Note(int semitonesFromA)
    {
        this.semitonesFromA = semitonesFromA;
    }

    // generic IComparable<T>
    public int CompareTo(Note other)
    {
        if (Equals(other))
            return 0;
        return SemitonesFromA.CompareTo(other.SemitonesFromA);
    }

    // non-generic IComaparable
    public int IComparable.CompareTo(object other)
    {
        if (!(other is Note))
            throw new InvalidOperationException("CompareTo: Not a note");
        return CompareTo((Note)other);
    }

    public static bool operator <(Note n1, Note n2)
    {
        return n1.CompareTo(n2) < 0;
    }

    public static bool operator >(Note n1, Note n2)
    {
        return n1.CompareTo(n2) > 0;
    }

    // for IEquatable
    public bool Equals(Note other)
    {
        return this.SemitonesFromA == other.SemitonesFromA;
    }

    // override Object.Equals
    public override bool Equals(object other)
    {
        if (!(other is Note))
            throw new InvalidOperationException("CompareTo: Not a note");
        return Equals((Note)other);
    }

    public override int GetHashCode()
    {
        return SemitonesFromA.GetHashCode();
    }

    public static bool operator ==(Note n1, Note n2)
    {
        return n1.Equals(n2);
    }

    public static bool operator !=(Note n1, Note n2)
    {
        return !(n1 == n2);
    }
}
时间: 2024-11-07 02:25:24

C#排序比较的相关文章

经典排序算法 - 冒泡排序Bubble sort

 原文出自于 http://www.cnblogs.com/kkun/archive/2011/11/23/bubble_sort.html 经典排序算法 - 冒泡排序Bubble sort 原理是临近的数字两两进行比较,按照从小到大或者从大到小的顺序进行交换, 这样一趟过去后,最大或最小的数字被交换到了最后一位, 然后再从头开始进行两两比较交换,直到倒数第二位时结束,其余类似看例子 例子为从小到大排序, 原始待排序数组| 6 | 2 | 4 | 1 | 5 | 9 | 第一趟排序(外循环) 第

算法 希尔排序

希尔排序 Shell Sort 介绍: 希尔排序(Shell Sort)也称缩小增量排序,是直接插入排序算法的一种更高效的改进版本. 该方法的基本思想是:先将整个待排元素序列分割成若干个子序列(由相隔某个"增量"的元素组成的)分别进行直接插入排序,然后依次缩减增量再进行排序,待整个序列中的元素基本有序(增量足够小)时,再对全体元素进行一次直接插入排序.因为直接插入排序在元素基本有序的情况下(接近最好情况),效率是很高的,因此希尔排序在时间效率比直接插入排序有较大提高. 执行流程: 首先

算法 排序NB二人组 堆排序 归并排序

参考博客:基于python的七种经典排序算法     常用排序算法总结(一) 序前传 - 树与二叉树 树是一种很常见的非线性的数据结构,称为树形结构,简称树.所谓数据结构就是一组数据的集合连同它们的储存关系和对它们的操作方法.树形结构就像自然界的一颗树的构造一样,有一个根和若干个树枝和树叶.根或主干是第一层的,从主干长出的分枝是第二层的,一层一层直到最后,末端的没有分支的结点叫做叶子,所以树形结构是一个层次结构.在<数据结构>中,则用人类的血统关系来命名,一个结点的分枝叫做该结点的"

数字在排序数组中出现的次数

题目:统计一个数字在排序数组中出现的次数.例如输入排序数组{1,2,3,3,3,3,4,5}和数字3,由于3在这个数组中出现了4次,因此输出4. 程序实现: import java.util.Scanner; public class Test38 { public static void main(String[] args) { /**  * 初始化数组,测试数据可以多种途径初始化  */   Scanner in = new Scanner(System.in); /*int[] a = 

算法 排序lowB三人组 冒泡排序 选择排序 插入排序

参考博客:基于python的七种经典排序算法   [经典排序算法][集锦]     经典排序算法及python实现 首先明确,算法的实质 是 列表排序.具体就是操作的列表,将无序列表变成有序列表! 一.排序的基本概念和分类 所谓排序,就是使一串记录,按照其中的某个或某些关键字的大小,递增或递减的排列起来的操作.排序算法,就是如何使得记录按照要求排列的方法. 排序的稳定性: 经过某种排序后,如果两个记录序号同等,且两者在原无序记录中的先后秩序依然保持不变,则称所使用的排序方法是稳定的,反之是不稳定

C/C++算法竞赛入门经典Page11 例题1-5 三整数排序

题目:输入3个整数,从小到大排序后输出 样例输入: 20 7 33 样例输出: 7 20 33 首先,先声明三个整数a,b,c和一个临时变量t: int a,b,c,t;//1,b,c为三个整数,t为临时变量 输入三个整数: scanf("%d%d%d",&a,&b,&c); 进行3次if判断: 1.如果b>a,则a,b对调 2.如果c>a,则a,c对调 3.如果c>b,则b,c对调 代码: if(b>=a){ t=b; b=a; a=t

排序与查找

1.冒泡法排序 2.选择排序 3.二分法查找(有序数组) 4.无序数组(基本查找)

使用插件bootstrap-table实现表格记录的查询、分页、排序等处理

在业务系统开发中,对表格记录的查询.分页.排序等处理是非常常见的,在Web开发中,可以采用很多功能强大的插件来满足要求,且能极大的提高开发效率,本随笔介绍这个bootstrap-table是一款非常有名的开源表格插件,在很多项目中广泛的应用.Bootstrap-table插件提供了非常丰富的属性设置,可以实现查询.分页.排序.复选框.设置显示列.Card view视图.主从表显示.合并列.国际化处理等处理功能,而且该插件同时也提供了一些不错的扩展功能,如移动行.移动列位置等一些特殊的功能,插件可

AC日记——双栈排序 洛谷 P1155

双栈排序 思路: 二分图染+模拟: 代码: #include <bits/stdc++.h> using namespace std; #define maxn 1005 #define maxm 2000005 int n,head[maxn],E[maxm],V[maxm],cnt,col[maxn]; int minn[maxn],ai[maxn],sta1[maxn],sta2[maxn],top1,top2; bool if_[maxn][maxn]; inline void in(

输入password登录到主界面,录入学生编号,排序后输出

n 题目:输入password登录到主界面,录入学生编号,排序后输出 n 1.  语言和环境 A.实现语言 C语言 B.环境要求 VC++ 6.0 n 2.  要求 请编写一个C语言程序.将若干学生编号按字母顺序(由小到大)输出. 程序的功能要求例如以下: 1)  输入password"admin",正确则进入主界面,错误则直接推出(exit(0)): 2)从键盘输入5个学生编号"BJS1001","BJS2001"."BJS1011&