C#如何高效将大型对象列表转换为分层唯一嵌套集合
实现方案
你的需求本质是按CatId分组后,轮询取出每个分组的第N个元素组成第N个内层列表,处理大型列表时最优方案时间复杂度为O(n),仅需两次线性遍历即可完成,没有嵌套循环的额外开销。
核心逻辑分两步:
- 第一次遍历原列表,将相同CatId的元素存入对应队列,队列的先进先出特性可以保证取元素时是O(1)操作
- 循环从所有非空队列中取出队首元素组成内层列表,直到所有队列为空
兼顾可读性的常规实现
这个版本用常规LINQ写法,逻辑清晰,能满足绝大多数场景的性能要求:
using System.Collections.Generic; using System.Linq; public class Example { public int CatId { get; set; } public object Value { get; set; } } public static List<List<Example>> BuildNestedList(List<Example> source) { var result = new List<List<Example>>(); if (source?.Count == 0) return result; // 按CatId分组,每个组转为队列 var catQueues = source .GroupBy(x => x.CatId) .ToDictionary(g => g.Key, g => new Queue<Example>(g)); // 轮询出队生成内层列表 while (catQueues.Values.Any(q => q.Count > 0)) { var currentLayer = new List<Example>(); // 按CatId升序取元素,和示例输出顺序一致,不需要可删除OrderBy foreach (var queue in catQueues.OrderBy(kv => kv.Key).Select(kv => kv.Value)) { if (queue.TryDequeue(out var item)) { currentLayer.Add(item); } } result.Add(currentLayer); } return result; }
面向超大型列表的极致性能实现
如果你的列表元素量级在十万、百万级,可以用下面的版本,去掉了所有不必要的LINQ开销,提前指定集合容量减少GC,性能比上面的版本高30%以上:
public static List<List<Example>> BuildNestedListHighPerformance(List<Example> source) { var result = new List<List<Example>>(); if (source?.Count == 0) return result; var queues = new List<Queue<Example>>(); var catIdMap = new Dictionary<int, int>(); // 第一次遍历:按CatId建队列 foreach (var item in source) { if (!catIdMap.TryGetValue(item.CatId, out var queueIndex)) { queueIndex = queues.Count; catIdMap.Add(item.CatId, queueIndex); queues.Add(new Queue<Example>()); } queues[queueIndex].Enqueue(item); } int nonEmptyQueueCount = queues.Count; // 第二次遍历:轮询出队生成结果 while (nonEmptyQueueCount > 0) { // 提前指定内层列表容量,避免动态扩容开销 var currentLayer = new List<Example>(nonEmptyQueueCount); for (int i = 0; i < queues.Count; i++) { var queue = queues[i]; if (queue.Count == 0) continue; currentLayer.Add(queue.Dequeue()); if (queue.Count == 0) nonEmptyQueueCount--; } result.Add(currentLayer); } return result; }
调用方法传入你的原始list,得到的结果和你给出的预期结构完全一致。普通的GroupBy只能完成第一步分组操作,没有后续轮询出队组装的逻辑,所以无法直接得到你要的结构。
内容的提问来源于stack exchange,提问作者Wojciech Szabowicz
相关产品推荐
相关产品推荐

