我有以下扩展方法将List<T>
拆分为具有不同块大小的List<T>
列表,但我怀疑其效率。我能做些什么来改善它,还是它现在就很好?
public static List<List<T>> Split<T>(this List<T> source, params int[] chunkSizes)
{
int totalSize = chunkSizes.Sum();
int sourceCount = source.Count();
if (totalSize > sourceCount)
{
throw new ArgumentException("Sum of chunk sizes is larger than the number of elements in source.", "chunkSizes");
}
List<List<T>> listOfLists = new List<List<T>>(chunkSizes.Length);
int index = 0;
foreach (int chunkSize in chunkSizes)
{
listOfLists.Add(source.GetRange(index, chunkSize));
index += chunkSize;
}
// Get the entire last part if the total size of all the chunks is less than the actual size of the source
if (totalSize < sourceCount)
{
listOfLists.Add(source.GetRange(index, sourceCount - totalSize));
}
return listOfLists;
}
示例代码用法:
List<int> list = new List<int> { 1,2,4,5,6,7,8,9,10,12,43,23,453,34,23,112,4,23 };
var result = list.Split(2, 3, 3, 2, 1, 3);
Console.WriteLine(result);
这得到了一个期望的结果,a有一个最终的列表部分,有4个数字,因为总块大小比我的列表大小小4。
我特别怀疑GetRange
部分,因为我担心这只是一遍又一遍地列举相同的来源…
EDIT:我想我知道一种枚举源一次的方法:只需对源本身进行foreach,并不断检查迭代元素的数量是否与当前块大小相同。如果是,则添加新列表并转到下一个块大小。想法吗?
这段代码没有性能问题。GetRange
被记录为0 (chunkSize),这也很容易推断,因为List<T>
最重要的属性之一就是它允许0(1)个索引。
也就是说,您可以像这样编写更LINQ-y版本的代码:
var rangeStart = 0;
var ranges = chunkSizes.Select(n => Tuple.Create((rangeStart += n) - n, n))
.ToArray();
var lists = ranges.Select(r => source.GetRange(r.Item1, r.Item2)).ToList();
if (rangeStart < source.Count) {
lists.Last().AddRange(source.Skip(rangeStart));
}
return lists;
我建议使用这种扩展方法将源列表按指定的块大小分成子列表:
using System.Collections.Generic;
using System.Linq;
…
/// <summary>
/// Helper methods for the lists.
/// </summary>
public static class ListExtensions
{
public static List<List<T>> ChunkBy<T>(this List<T> source, int chunkSize)
{
return source
.Select((x, i) => new { Index = i, Value = x })
.GroupBy(x => x.Index / chunkSize)
.Select(x => x.Select(v => v.Value).ToList())
.ToList();
}
}