次のコードでは、メソッドが安価になるようにSelect()
リストのサイズを内部のどこかに保持するのに十分スマートなメソッドですか?ToArray()
List<Thing> bigList = someBigList;
var bigArray = bigList.Select(t => t.SomeField).ToArray();
これは、実装を見なくても簡単に確認できます。を実装するクラスを作成し、プロパティIList<T>
にトレースを入れるだけです。Count
class MyList<T> : IList<T>
{
private readonly IList<T> _list = new List<T>();
public IEnumerator<T> GetEnumerator()
{
return _list.GetEnumerator();
}
public void Add(T item)
{
_list.Add(item);
}
public void Clear()
{
_list.Clear();
}
public bool Contains(T item)
{
return _list.Contains(item);
}
public void CopyTo(T[] array, int arrayIndex)
{
_list.CopyTo(array, arrayIndex);
}
public bool Remove(T item)
{
return _list.Remove(item);
}
public int Count
{
get
{
Console.WriteLine ("Count accessed");
return _list.Count;
}
}
public bool IsReadOnly
{
get { return _list.IsReadOnly; }
}
public int IndexOf(T item)
{
return _list.IndexOf(item);
}
public void Insert(int index, T item)
{
_list.Insert(index, item);
}
public void RemoveAt(int index)
{
_list.RemoveAt(index);
}
public T this[int index]
{
get { return _list[index]; }
set { _list[index] = value; }
}
#region Implementation of IEnumerable
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
#endregion
}
プロパティがアクセスされた場合Count
、このコードは「アクセス数」を出力する必要があります。
var list = new MyList<int> { 1, 2, 3 };
var array = list.Select(x => x).ToArray();
しかし、何も出力しないので、いいえ、カウントを追跡しません。もちろん、 に固有の最適化が存在する可能性がありますが、List<T>
可能性は低いと思われます...
いいえ、今のところそうではありません (少なくとも .NET 実装では)。MS 参照ソースから、次のEnumerable.ToArray
ように実装されます。
public static TSource[] ToArray<TSource>(this IEnumerable<TSource> source) {
if (source == null) throw Error.ArgumentNull("source");
return new Buffer<TSource>(source).ToArray();
}
Buffer<TSource>
必要に応じて反復およびサイズ変更することにより、構築時にソース シーケンスのコピーを (配列形式で) 作成します。が の場合、特別な「高速パス」source
がありますが、当然のことICollection<TSource>
ながら、の結果はEnumerable.Select
そのインターフェイスを実装していません。
とはいえ、純粋な好奇心以外では、この結果に何の意味もないと思います。1 つには、実装は将来の任意の時点で変更される可能性があります (費用対効果を簡単に分析しても、その可能性は低いと思われます)。いずれにせよ、最大で O(logN) の再割り当てに苦しむことになります。N が小さい場合、再割り当ては目立たなくなります。N が大きい場合、コレクションの反復処理に費やされる時間は O(N) になるため、簡単に支配されます。
演算子を列挙可能なシーケンスに適用するSelect
と、次の反復子のいずれかが作成されます。
WhereSelectArrayIterator
WhereSelectListIterator
WhereSelectEnumerableIterator
の場合List<T>
、WhereSelectListIterator
イテレータが作成されます。リストの反復子を使用してリストを反復処理し、述語とセレクターを適用します。これはMoveNext
メソッドの実装です:
while (this.enumerator.MoveNext())
{
TSource current = this.enumerator.Current;
if ((this.predicate == null) || this.predicate(current))
{
base.current = this.selector(current);
return true;
}
}
ご覧のとおり、predicate に一致したアイテムの数に関する情報は保持されないため、フィルター処理されたシーケンス内のアイテムの数はわかりません。