在C#中,至少有两种不同的方法来“切片”数组。可以使用Linq的Skip()和Take()方法(也适用于其他集合类型),如下所示。..myArr.Skip(10).Take(20);...或者可以使用新的System.Index和System.Range类型:myArr[10..20]假设底层集合是可索引/物化的集合,如Array或List<T>,那么范围索引是否比Skip/Take提供了性能改进?
Skip()
Take()
myArr.Skip(10).Take(20);
System.Index
System.Range
myArr[10..20]
Array
List<T>
o7jaxewo1#
调用Take<T>(int)将在此实现中结束:
Take<T>(int)
private static IEnumerable<TSource> TakeIterator<TSource>(IEnumerable<TSource> source, int count) { return source is IPartition<TSource> partition ? partition.Take(count) : source is IList<TSource> sourceList ? new ListPartition<TSource>(sourceList, 0, count - 1) : new EnumerablePartition<TSource>(source, 0, count - 1); }
我们可以更深入地挖掘,但最后很明显,当使用ListPartition<T>作为List或数组时,Take和Skip都是O(1),这是我们所期望的。它需要分配一个临时迭代器示例,但不复制数据。EnumerablePartition<T>的实现稍微复杂一些,因此在最坏情况下每步的迭代时间为O(n),但它也不会创建数据的副本。另一方面,如果你这样做
ListPartition<T>
EnumerablePartition<T>
int[] data = new data[30]; int[] result = data[10..20];
这在内部被转换为对T[] RuntimeHelpers.GetSubArray<T>(T[] array, Range range)的调用。已经从签名中可以看出,这创建了数组段的副本。请注意,与Linq操作符不同,范围操作符(无论出于何种原因)仅在数组上受支持,而在IList上不受支持。
T[] RuntimeHelpers.GetSubArray<T>(T[] array, Range range)
1条答案
按热度按时间o7jaxewo1#
调用
Take<T>(int)
将在此实现中结束:我们可以更深入地挖掘,但最后很明显,当使用
ListPartition<T>
作为List或数组时,Take和Skip都是O(1),这是我们所期望的。它需要分配一个临时迭代器示例,但不复制数据。EnumerablePartition<T>
的实现稍微复杂一些,因此在最坏情况下每步的迭代时间为O(n),但它也不会创建数据的副本。另一方面,如果你这样做
这在内部被转换为对
T[] RuntimeHelpers.GetSubArray<T>(T[] array, Range range)
的调用。已经从签名中可以看出,这创建了数组段的副本。请注意,与Linq操作符不同,范围操作符(无论出于何种原因)仅在数组上受支持,而在IList上不受支持。