我有一个内存列表,其中包含大约100万条包含多列的记录(日期,名称,值,ID,.....)
我的问题:通过多列对这么大的列表进行排序的最佳解决方案是什么(即提供最佳性能)?
示例(伪代码):
list.orderbyDesc(name).thenBy(Name).thenBy(Value)
答案 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;
}
}