C#中如何高效获取List的子列表?
C#中如何高效获取List的子列表?
我太懂这种无奈了!数组能用[0..1]这种范围运算符轻松切出子数组,但List<T>偏偏不支持这个语法,自己写的方法还要来回转数组,确实有点折腾。给你几个更高效的方案:
1. 用原生的GetRange方法(推荐,.NET 2.0+就支持)
很多人可能没注意到,List<T>本身就有一个专门取子列表的方法——GetRange,它内部直接操作底层数组,效率很高,不需要额外的数组转换。不过要注意它的参数是起始索引和要获取的元素数量,而不是结束索引:
List<string> stringList = new(){"2", "a", "dummy-data"}; // 对应数组的[0..1](左闭右开,取索引0的元素),这里count是1 var subList = stringList.GetRange(0, 1);
如果想和数组的范围语法对齐,可以加个扩展方法适配Range:
public static class ListExtensions { public static List<T> SubList<T>(this List<T> list, Range range) { var (offset, length) = range.GetOffsetAndLength(list.Count); return list.GetRange(offset, length); } } // 这样就能像数组一样用了: var subList = stringList.SubList(0..1);
2. 用LINQ的Skip+Take(简洁通用)
如果不想写扩展方法,用LINQ的组合操作也很方便,代码可读性高,而且适用于所有IEnumerable<T>类型:
// 同样对应[0..1],Skip跳过0个元素,Take取1个元素 var subList = stringList.Skip(0).Take(1).ToList();
不过要注意,LINQ的Skip和Take会遍历元素,虽然对于List<T>来说性能差异不大,但如果是大数据量的场景,GetRange会更高效。
3. 优化你的扩展方法(避免不必要的数组转换)
如果你坚持自己写扩展方法,完全可以去掉转数组的步骤,直接遍历需要的元素添加到新列表,这样能节省内存和转换开销:
public static class ListExtensions { public static IList<T> SubList<T>(this IList<T> list, int startIndex, int endIndex) { // 先做参数校验,防止越界 if (startIndex < 0 || endIndex > list.Count || startIndex >= endIndex) { throw new ArgumentOutOfRangeException(nameof(startIndex)); } // 预先设置容量,避免列表扩容 var result = new List<T>(endIndex - startIndex); for (int i = startIndex; i < endIndex; i++) { result.Add(list[i]); } return result; } // 同样加个Range重载,方便使用范围语法 public static IList<T> SubList<T>(this IList<T> list, Range range) { var (offset, length) = range.GetOffsetAndLength(list.Count); return list.SubList(offset, offset + length); } }
这样优化后,就不会有多余的数组转换了,性能和原生的GetRange差不多,还能支持所有IList<T>类型。
内容来源于stack exchange
相关产品推荐
相关产品推荐

