分組統計是常見需求 (如反洗錢大額交易統計)。本文以「已排序資料按 Key 分組,計算各組筆數與平均值」為例,比較三種寫法的程式碼結構與效能。
IEnumerable<Tuple<int, double>> GetTuples(int n)
{
var tuples = new Tuple<int, double>[n];
var rand = new Random();
for (int k = 1, i = 0; i < n; i++)
{
var r = rand.Next(n);
k += (r >= n - 3) ? 2 : ((r >= n - 9) ? 1 : 0);
tuples[i] = new Tuple<int, double>(k, rand.NextDouble());
}
return tuples;
}
產生 n 筆已依 Key 排序的資料。
MoveNext,將「組別變更」與「結束」邏輯合併,消除重複代碼。from k in tuples group k by k.Item1,但執行階段與記憶體開銷最大。// Foreach
foreach (var v in tuples) { if (key != v.Item1) { if (key != null) result.Add(...); sum = count = 0; key = v.Item1; } count++; sum += v.Item2; }
if (key != null) result.Add(...);
// Iterator
for (var iter = tuples.GetEnumerator(); ; count++, sum += iter.Current.Item2)
{
var hasValue = iter.MoveNext();
if (!hasValue || key != iter.Current.Item1)
{
if (key != null) result.Add(...);
if (!hasValue) break;
sum = count = 0; key = iter.Current.Item1;
}
}
// LINQ
var q = from k in tuples group k by k.Item1;
foreach (var g in q) result.Add(new Tuple<int, int, double>(g.Key, g.Count(), g.Average(v => v.Item2)));
三種方法輸出結果完全一致 (12 組,總筆數 10,485,760,整體平均 0.500189)。但執行速度與記憶體佔用:
資料已排序時,Iterator 寫法最優雅且高效;未排序需先 OrderBy。LINQ 適合快速開發、資料量不大的場合。文中附完整主程式與 Tuple 定義 (.NET 4.0 內建 Tuple 可直接使用)。