翻閱《C# Cookbook》第 4.8 節「反轉 Sorted List 裡的內容」,發現一個近 1300 行的 ReversibleSortedList 範例。代碼品質極高,值得深度剖析。雖然實務專案中直接使用 FCL 現成集合(SortedDictionary、SortedList)即可,但手寫核心集合類別能:
需求:存儲鍵值對(TKey, TValue),類型不固定 → 泛型。
儲存容器選擇:陣列。理由:存取速度最快、類型安全、語法簡潔。唯一缺點——容量固定,需自行實作動態擴容。
public class ReversibleSortedList<TKey, TValue>
{
private TKey[] keys;
private TValue[] values;
public int Capacity => this.keys.Length;
}
Capacity 會拋出 NullReferenceExceptionkeys 未初始化,無法存取 Length。
| 方案 | 代碼 | 優點 | 缺點 |
|---|---|---|---|
| 宣告時初始化空陣列 | private TKey[] keys = new TKey[0]; |
簡單可行 | 一聲明即成垃圾,感覺不潔 |
| 預設給固定容量(如 4) | private TKey[] keys = new TKey[4]; |
避免頻繁擴容 | 浪費 4 個槽位空間,違背完美主義 |
| 靜態共享空陣列(採用) | private static TKey[] emptyKeys; |
全程序僅一份空陣列,零浪費、零重複分配 | 需在靜態建構子初始化 |
#region 成員變數
private TKey[] keys;
private TValue[] values;
private static TKey[] emptyKeys;
private static TValue[] emptyValues;
#endregion
#region 建構子
static ReversibleSortedList() // 靜態建構子(型別建構子)
{
emptyKeys = new TKey[0];
emptyValues = new TValue[0];
}
public ReversibleSortedList() // 實例建構子
{
this.keys = emptyKeys;
this.values = emptyValues;
}
#endregion
public int Capacity => this.keys.Length;
public class Test
{
static void Main()
{
var rs = new ReversibleSortedList<int, string>();
Console.WriteLine(rs.Capacity); // 輸出 0
}
}
執行結果:0 —— 完美運作,無例外、無多餘分配。
Array.Empty<T>()(.NET Core 2.0+)內部即採用此模式,我們手寫版本等同於早期 FCL 的最佳實踐。keys/values 平行陣列設計,後續排序、插入、刪除時需同步搬移兩個陣列,增加複雜度但極大提升記憶體局部性與快取命中率。下一篇將引入 Array.BinarySearch 實現 O(log n) 插入點查找,並設計支援升/降序切換的比較器架構。