.NET Framework 提供兩種清單實作:鏈結清單 與 動態陣列。本文透過嚴謹的效能基準測試,在佇列(FIFO)、堆疊(LIFO)、單純尾端插入三大基礎演算法場景下,對比兩者在不同資料量級下的真實表現,並給出明確的選型建議。
| 演算法 / 初始容量 | 0 | 64 | 255 | 1024 |
|---|---|---|---|---|
| 佇列 | 35837 | 35753 | 37171 | 35711 |
| 堆疊 | 302 | 279 | 298 | 289 |
| 單純插入 | 100 | 105 | 100 | 100 |
結論:初始容量對 List 效能無顯著影響(佇列測試波動屬測試誤差範圍)。
測試邏輯:AddLast 入隊 → RemoveFirst 出隊,循環 1000 次
| 清單類型 / 最大長度 | 10 | 30 | 50 | 100 | 1000 | 10000 |
|---|---|---|---|---|---|---|
| LinkedList | 0.77 | 0.88 | 1.10 | 2.04 | 61 | 691 |
| List | 0.33 | 1.17 | 2.00 | 12 | 443 | 37516 |
分析:
RemoveAt(0) 觸發整個陣列記憶體搬移測試邏輯:AddLast 入棧 → RemoveLast 出棧,循環 1000 次
| 清單類型 / 最大長度 | 10 | 30 | 50 | 100 | 1000 | 10000 |
|---|---|---|---|---|---|---|
| LinkedList | 0.85 | 0.93 | 1.11 | 2.19 | 58 | 714 |
| List | 0.11 | 0.31 | 0.51 | 1.23 | 29 | 284 |
分析:兩者皆線性增長,但 List 全程領先。原因:RemoveAt(Count-1) 僅調整 Count,無記憶體搬移、無重新配置(除非主動 TrimExcess)。LinkedList 仍需節點建立/銷毀開銷。
測試邏輯:AddLast 循環插入 → Clear,循環 1000 次
| 清單類型 / 最大長度 | 10 | 30 | 50 | 100 | 1000 | 10000 |
|---|---|---|---|---|---|---|
| LinkedList | 0.68 | 0.77 | 0.94 | 1.78 | 40 | 535 |
| List | 0.09 | 0.17 | 0.25 | 0.43 | 10 | 109 |
結論:與堆疊結果高度一致,List 尾端插入極致高效(摊還 O(1)),LinkedList 節點開銷固定存在。
class Program {
const int TEST_TIMES = 1000;
static void Main() {
// 容量影響測試
TestQueue(10000, 0); TestQueue(10000, 64); TestQueue(10000, 255); TestQueue(10000, 1024);
TestStack(10000, 0); TestStack(10000, 64); TestStack(10000, 255); TestStack(10000, 1024);
TestInsert(10000, 0); TestInsert(10000, 64); TestInsert(10000, 255); TestInsert(10000, 1024);
// 核心對比測試
foreach (int n in new[]{10,30,50,100,1000,10000}) {
TestQueue(n); TestStack(n); TestInsert(n);
}
}
// TestQueue, TestStack, TestInsert 方法完整實作見原文
// 關鍵:使用 Stopwatch 高精度計時,ElaspedTicks 轉換毫秒
}
| 演算法場景 | 推薦類型 | 條件限制 |
|---|---|---|
| 佇列 (FIFO) | LinkedList | 長度 ≥ 30;長度 < 30 可用 List |
| 堆疊 (LIFO) | List | 無特殊限制 |
| 單純尾端插入/清單建構 | List | 無特殊限制 |
| 記憶體受限環境 | LinkedList | 無額外預留空間,節點緊湊 |
效能測試揭示了資料結構本質:陣列尾端操作極快、頭部操作極慢;鏈結清單頭尾操作均衡但固定開銷較大。實務選型請依據演算法存取模式與資料量級決策,而非習慣性預設使用 List<T>。