浅谈C#容器类型数据排序

0. 完整代码链接

链接:https://pan.baidu.com/s/1NibsPHraeUFSq1uxuioMdQ?pwd=hyw3 
提取码:hyw3

1. 基本值类型数据排序(引言)

我们先来看int型数组排序

    int[] nums = { 1, 5, 3, 9, 7 };
    
    Console.WriteLine("--- 排序前: ---" + "\n");
    foreach (int i in nums) 
    {
        Console.Write(i + " ");
    }
    
    Array.Sort(nums);
    
    Console.WriteLine("\n");
    Console.WriteLine("--- 排序后: ---" + "\n");
    
    foreach (int i in nums)
    {
        Console.Write(i + " ");
    }

输出:

没有问题

2. 那么问题来了,自定义数据类型可以这么排序吗???

先来看代码 

1. Student.cs

public class Student
{
    public int Id { get; set; }
    public float Score { get; set; }

    public override string ToString()
    {
        return $"Id: {Id}, Score: {Score}";
    }
}

2. Main.cs

Student[] stus =
{
    new Student(){ Id = 1, Score = 80},
    new Student(){ Id = 3, Score = 60},
    new Student(){ Id = 5, Score = 90},
    new Student(){ Id = 2, Score = 100},
    new Student(){ Id = 4, Score = 50},
};
Array.Sort(stus);

运行主函数,报异常

这个异常叫做无效操作异常,或者叫错误操作异常
原因是
在数组中失败的比较了两个元素,
内部异常 —— 参数异常:至少一个对象必须实现可比较接口(IComparable)

3. 那么问题又来了,为什么基本数据类型就可以直接排序,自定义类型就不可以呢???

通过查看 int 源码可以发现

原来int实现了接口IComparable,所以可以排序

这个接口有一个方法

//
// Summary:
//    Defines a generalized comparison method that a value type or class implements
//    to create a type-specific comparison method for ordering or sorting its instances.
//    
//    定义值类型或类实现的通用比较方法,以创建特定于类型的比较方法用于排序,或排序它的实例。
//
// Type parameters:
//    T:
//    The type of object to compare.
//    需要比较的对象类型
public interface IComparable<in T>
{
    //
    // Summary:
    //    Compares the current instance with another object of the same type and returns
    //    an integer that indicates whether the current instance precedes, follows, or
    //    occurs in the same position in the sort order as the other object.
    //    将当前实例与另一个相同类型的对象进行比较,并返回一个整数,该整数指示当前实例在排序顺序        
    //    上是在另一个对象之前、之后还是出现在相同的位置。
    //
    // Parameters:
    //    other:
    //    An object to compare with this instance.
    //    和这个实例比较的一个对象
    // Returns:
    //    A value that indicates the relative order of the objects being compared. The
    //    return value has these meanings:
    //    指示被比较对象的相对顺序的值。
    //    返回值有以下含义:

    //    Value – Meaning(值含义)
    //    Less than zero – This instance precedes other in the sort order.
    //    小于零 - 此实例在排序顺序上位于其他实例之前。
    
    //    Zero – This instance occurs in the same position in the sort order as other.              
    //    0 - 此实例在排序顺序中出现在与其他实例相同的位置。

    //    Greater than zero – This instance follows other in the sort order.
    //    大于零 - 这个实例按照排序顺序跟在其他实例后面。
    int CompareTo(T? other);
}

而 int 重写了这个方法

public int CompareTo(object? value)
{
    if (value == null)
    {
        return 1;
    }
    if (value is int i)
    {
        if (m_value < i) return -1;
        if (m_value > i) return 1;
        return 0;
    }
    throw new ArgumentException(SR.Arg_MustBeInt32);
}

所以可以排序,既然如此自定义类型 Student 也实现这个接口(IComparable)不就可以了吗

代码如下:

public class Student : IComparable<Student>
{
    public int Id { get; set; }
    public float Score { get; set; }

    public override string ToString()
    {
        return $"Id: {Id}, Score: {Score}";
    }

    public int CompareTo(Student? other)
    {
        if (other == null) return -1;
        return this.Score > other.Score ? 1 : -1;
    }
}

运行如下代码:

Student[] stus =
{
    new Student(){ Id = 1, Score = 80},
    new Student(){ Id = 3, Score = 60},
    new Student(){ Id = 5, Score = 90},
    new Student(){ Id = 2, Score = 100},
    new Student(){ Id = 4, Score = 50},
};

Console.WriteLine("排序前: ");
foreach (Student stu in stus) 
{
    Console.WriteLine(stu);
}

Array.Sort(stus);

Console.WriteLine("排序后: ");
foreach (Student stu in stus)
{
    Console.WriteLine(stu);
}

结果图

这里按照学生成绩升序排序!!!

完美解决!

4. 问题又来了,C#功能这么强大,Sort方法难道没有重载?

翻看源码得知,原来Sort有这么多重载方法

 其中有一个方法Sort(Array array, IComparer? comparer),这是什么?

5. IComparer比较器

翻看IComparer源码发现这是一个接口,里面有一个方法

public interface IComparer<in T>
{
    int Compare(T? x, T? y);
}

能否如法炮制,自定义一个类传入这个接口对象,实现自定义类型排序呢

答案肯定可以!

public class StudentComparer : IComparer<Student>
{
    public int Compare(Student? x, Student? y)
    {
        if (x == null || y == null) return 0;
        return x.Score.CompareTo(y.Score);
    }
}

6. CompareTo方法源码

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int CompareTo(float value)
{
    if (m_value < value) return -1;
    if (m_value > value) return 1;
    if (m_value == value) return 0;
    // At least one of the values is NaN.
    if (IsNaN(m_value))
        return IsNaN(value) ? 0 : -1;
    else // f is NaN.
        return 1;
}

Student.cs

public class Student
{
    public int Id { get; set; }
    public float Score { get; set; }

    public override string ToString()
    {
        return $"Id: {Id}, Score: {Score}";
    }
}

Main.cs

Student[] stus =
{
    new Student(){ Id = 1, Score = 80},
    new Student(){ Id = 3, Score = 60},
    new Student(){ Id = 5, Score = 90},
    new Student(){ Id = 2, Score = 100},
    new Student(){ Id = 4, Score = 50},
};

Console.WriteLine("排序前: ");
foreach (Student stu in stus)
{
    Console.WriteLine(stu);
}

Array.Sort(stus, new StudentComparer());

Console.WriteLine("排序后: ");
foreach (Student stu in stus)
{
    Console.WriteLine(stu);
}

输出结果与 标题3结果图一致

7. 面向接口编程

前面两种方法从概念来说都属于面向接口编程

一个是类实现IComparable

一个是类实现IComparer

8. 用委托实现

 其实还提供了一个 Sort 方法

public static void Sort<T>(T[] array, Comparison<T> comparison)

这里 Comparison 是一个委托

public delegate int Comparison<in T>(T x, T y);

所以代码可以这么改写:

public static void Main(string[] args)
{
    Student[] stus =
    {
        new Student(){ Id = 1, Score = 80},
        new Student(){ Id = 3, Score = 60},
        new Student(){ Id = 5, Score = 90},
        new Student(){ Id = 2, Score = 100},
        new Student(){ Id = 4, Score = 50},
    };

    Console.WriteLine("排序前: ");
    foreach (Student stu in stus)
    {
        Console.WriteLine(stu);
    }

    Array.Sort(stus, CompareByScore);

    Console.WriteLine("排序后: ");
    foreach (Student stu in stus)
    {
        Console.WriteLine(stu);
    }
}
static int CompareByScore(Student a, Student b)
{
    return a.Score > b.Score ? 1 : -1;
}

这里CompareByScore不一定要被static修饰,因为Main被static修饰了,所以CompareByScore也要被static修饰,否则会报错,因为静态方法里不能调用实例方法

输出结果:

9. 用Lambda表达式实现

委托都用了,Lambda表达式更可以用!

改写代码如下

Student[] stus =
{
    new Student(){ Id = 1, Score = 80},
    new Student(){ Id = 3, Score = 60},
    new Student(){ Id = 5, Score = 90},
    new Student(){ Id = 2, Score = 100},
    new Student(){ Id = 4, Score = 50},
};

Console.WriteLine("排序前: ");
foreach (Student stu in stus)
{
    Console.WriteLine(stu);
}

Array.Sort(stus, (Student stu1, Student stu2) =>
{
    return stu1.Score > stu2.Score ? 1 : -1;
});

Console.WriteLine("排序后: ");
foreach (Student stu in stus)
{
    Console.WriteLine(stu);
}

输出结果不变:

10. (扩展)冒泡排序

10.1 普通版

void BubbleSort(int[] nums)
{
    int len = nums.Length;
    for (int i = 0; i < len - 1; i++)
    {
        for (int j = 0; j < len - i - 1; j++)
        {
            if (nums[j] > nums[j + 1])
            {
                int temp = nums[j];
                nums[j] = nums[j + 1];
                nums[j + 1] = temp;
            }
        }
    }
}

看了这篇帖子,以后就不要这么写了,不通用!

10.2 通用版

/// <summary>
/// 这里condition作为条件委托参数传入, 您可以自定义专属于自己的冒泡排序条件!!!
/// </summary>
/// <typeparam name="T"> 参数类型 </typeparam>
/// <param name="array"> 数组变量名 </param>
/// <param name="contion"> 条件 </param>
static void BubbleSort<T>(T[] array, Func<T, T, bool> condition)
{
    int len = array.Length;
    for (int i = 0; i < len - 1; i++)
    {
        for (int j = 0; j < len - i - 1; j++)
        {
            if (condition(array[j], array[j + 1]))
            {
                T temp = array[j];
                array[j] = array[j + 1];
                array[j + 1] = temp;
            }
        }
    }
}

10.3 测试代码

int[] nums = { 1, 5, 7, 3, 9 };

Console.WriteLine("排序前: ");
foreach (var item in nums)
{
    Console.Write(item + " ");
}

BubbleSort<int>(nums, (a, b) =>
{
    return a > b;
});

Console.WriteLine("\n\n排序后: ");
foreach (var item in nums)
{
    Console.Write(item + " ");
}

输出结果:

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值