標籤:strong tostring work add 結構 range readline png tac
1. 源起:
KV 7.0加入列表管理功能,處理排序問題時,對空列表執行按大小、日期、長度排序時,其中次序會發生改變,令人納悶。
沒天理呀,不應該啊!List.Sort()方法,它為什嗎?
對此問題,深入去瞭解,倒發現了有趣的問題:穩固排序與非穩固排序。
2、穩固排序與非穩固排序
在微軟官方網站找到此段說明:
Remarks
If comparison is provided, the elements of the List<T> are sorted using the method represented by the delegate.
If comparison is null, an ArgumentNullException is thrown.
This method uses Array.Sort, which applies the introspective sort as follows:
If the partition size is fewer than 16 elements, it uses an insertion sort algorithm
If the number of partitions exceeds 2 * LogN, where N is the range of the input array, it uses a Heapsort algorithm.
Otherwise, it uses a Quicksort algorithm.
This implementation performs an unstable sort; that is, if two elements are equal, their order might not be preserved. In contrast, a stable sort preserves the order of elements that are equal.
On average, this method is an O(n log n) operation, where n is Count; in the worst case it is an O(n ^ 2) operation.
大意是此實現將執行不穩定排序。也就是說,如果兩個元素相等,則可能不會保留其順序。
我們建Demo做驗證,用例如下:
var list = new List<string>(); list.AddRange(new string[] { "3", "2", "1", "4" }); list.Sort((x, y) => { return 0; }); foreach (string s in list) Console.WriteLine(s);
其輸出為:
驗證上述結果,推測它是使用了二分法快速反序,把後半部分置前了。
可八輩的我們寧願它輸入為原始順序3214,這個合理,但是,可是,它無恥的變了……
3、實際情境
比如,我們需要對特定的對象進行排序,如下摘取網上例子稍加修正,以做樣本:
static void Main(string[] args) { var p1 = new Person() { Name = "Abby", Age = 38 }; var p4 = new Person() { Name = "Jason", Age = 23 }; var p2 = new Person() { Name = "Bob", Age = 23 }; var p3 = new Person() { Name = "Charlie", Age = 23 }; var p5 = new Person() { Name = "Danielle", Age = 18 }; var list = new List<Person>(); list.Add(p1); list.Add(p2); list.Add(p3); list.Add(p4); list.Add(p5); list.Sort(); Console.WriteLine("Unstable List Sort:"); foreach (Person p in list) Console.WriteLine(p); Console.ReadLine(); } class Person : IComparable { public string Name { get; set; } public int Age { get; set; } public int CompareTo(object obj) { int result = 1; if (obj != null && obj is Person) { var person = (Person)obj; result = this.Age.CompareTo(person.Age);; } return result; } public override string ToString() { return String.Format("{0} - {1}", this.Name, this.Age); } }
其輸出為:
其中間23歲之三項,沒有按Jason、Bob、Charlie的次序來,甚至也不是倒序。
我就是想要它加入順序不變,怎麼辦?
對此問題,搜出一些文章,有建議用LINQ排序因為它是穩固排序,可我們工程所用.net framework 2.0,不支援,只好做罷。
在stackoverflow上,碰到一群難兄難弟,諸般建議閱過,找到可用方法,加索引!
4、解決方案
且修改Person類,加入SortIndex屬性如下,並修正其比較函數:
class Person : IComparable { public string Name { get; set; } public int Age { get; set; } public int SortIndex { get; set; }
public int CompareTo(object obj) { int result = 1; if (obj != null && obj is Person) { var person = (Person)obj; result = this.Age.CompareTo(person.Age); if (result == 0) result = this.SortIndex.CompareTo(person.SortIndex); } return result; } ... }
初始化時,加入其索引:
var p1 = new Person() { Name = "Abby", Age = 38, SortIndex = 0 }; var p4 = new Person() { Name = "Jason", Age = 23, SortIndex = 1 }; var p2 = new Person() { Name = "Bob", Age = 23, SortIndex = 2 }; var p3 = new Person() { Name = "Charlie", Age = 23, SortIndex = 3 }; var p5 = new Person() { Name = "Danielle", Age = 18, SortIndex = 4 };
輸出順序:
保留初始順序,解決問題。
後記:
之前未曾留意,此問題發現倒是很有意思,度娘不給力,解決問題還是要google,在stackoverflow上找到有同困擾之人,得其可參考方案。
此情境,通常用於自訂資料結構比較,如KV項目之空白列表,當其可比較項相等時,應該保留其原始順序,可是被改變了,導致尋求解決問題的方法。
但官方方案固定,不能改變情況下,做個曲線救國,加另一索引以固定次序,雖然看來費些點工夫,但能解決問題,不失為好的可用方法。
參考文檔:
List(T).Sort Method (Comparison(T)) (System.Collections.Generic)
C# Stable Sort : C# 411
Why does List<T>.Sort method reorder equal IComparable<T> elements?
c#: List.Sort()實現穩固排序(stable sort)