按多个标准对List <t>进行排序的首选方法是什么?

时间:2016-12-07 21:00:43

标签: c#

我有一个内存列表,其中包含大约100万条包含多列的记录(日期,名称,值,ID,.....)

我的问题:通过多列对这么大的列表进行排序的最佳解决方案是什么(即提供最佳性能)?

示例(伪代码):

list.orderbyDesc(name).thenBy(Name).thenBy(Value)

1 个答案:

答案 0 :(得分:1)

原则上有两种方法可以对通用列表进行排序:

  • 使用Linq方法链:

    var orderedEnumerable = list.OrderByDescending(item => item.Property0)
        .ThenBy(item => item.Property1)
        .ThenBy(item => item.Property2);
    
  • 使用实施IComparer<T>的自定义比较器。使用比较器,您可以创建一个新的有序可枚举

    var orderedEnumerable = list.OrderBy(item => item, new MyComparer());
    

    或者您可以使用Sort()方法进行就地排序:

    list.Sort(new MyComparer());
    

尝试对不同方法进行基准测试(提示:我希望原位List<T>.Sort(IComparer<T>)原则上运行得最快,但这取决于您是否要枚举Enumerable.OrderBy的整个结果

以下是一个简单的基准样本:

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading;

class Record
{
    public string Name { get; set; }

    public int Age { get; set; }

    public double Salary { get; set; }
}


class RecordComparer : IComparer<Record>
{
    public int Compare(Record x, Record y)
    {
        // Sort by Name, Age, and then Salary
        if (x.Name != y.Name) return x.Name.CompareTo(y.Name);
        if (x.Age != y.Age) return x.Age.CompareTo(y.Age);
        return x.Salary.CompareTo(y.Salary);
    }
}

class Program
{
    static Random _random = new Random();
    static List<Record> _list;

    static void Main(string[] args)
    {
        Profile("SortUsingLinqMethodChain", 50, InitList, SortUsingLinqMethodChain);
        Profile("SortUsingLinqComparer", 50, InitList, SortUsingLinqComparer);
        Profile("SortUsingListSort", 50, InitList, SortUsingListSort);
    }

    static void InitList()
    {
        _list = new List<Record>();

        for (int i = 0; i < 10000; i++)
        {
            _list.Add(new Record { Name = RandomString(12), Age = RandomAge() });
        }
    }

    static void SortUsingLinqMethodChain()
    {
        // NOTE: the `ToList` materialization may not be necessary at all
        //    This totally depends on what you want to do with the result.
        _list = _list.OrderBy(item => item.Name)
                     .ThenBy(item => item.Age)
                     .ThenBy(item => item.Salary).ToList();
    }

    static void SortUsingLinqComparer()
    {
        // NOTE: the `ToList` materialization may not be necessary at all
        //    This totally depends on what you want to do with the result.
        _list = _list.OrderBy(item => item, new RecordComparer()).ToList();
    }

    static void SortUsingListSort()
    {
        _list.Sort(new RecordComparer());
    }

    // based on http://stackoverflow.com/a/1344242/40347
    public static string RandomString(int length)
    {
        const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
        return new string(Enumerable.Repeat(chars, length)
          .Select(s => s[_random.Next(s.Length)]).ToArray());
    }

    public static int RandomAge()
    {
        return _random.Next(100) + 1;
    }

    public static double RandomSalary()
    {
        return _random.NextDouble() * 100000;
    }

    // based on http://stackoverflow.com/a/1048708/40347
    static double Profile(string description, int iterations, Action init, Action func)
    {
        // Run at highest priority to minimize fluctuations 
        // caused by other processes/threads
        Process.GetCurrentProcess().PriorityClass = ProcessPriorityClass.High;
        Thread.CurrentThread.Priority = ThreadPriority.Highest;

        // warm up 
        init();
        func();

        var watch = new Stopwatch();

        // clean up
        GC.Collect();
        GC.WaitForPendingFinalizers();
        GC.Collect();

        for (int i = 0; i < iterations; i++)
        {
            init();
            watch.Start();
            func();
            watch.Stop();
        }

        Console.Write(description);
        Console.WriteLine(" Time Elapsed {0} ms", watch.Elapsed.TotalMilliseconds);
        return watch.Elapsed.TotalMilliseconds;
    }
}