3

次のコードでは、メソッドが安価になるようにSelect()リストのサイズを内部のどこかに保持するのに十分スマートなメソッドですか?ToArray()

List<Thing> bigList = someBigList;
var bigArray = bigList.Select(t => t.SomeField).ToArray();
4

3 に答える 3

5

これは、実装を見なくても簡単に確認できます。を実装するクラスを作成し、プロパティ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>可能性は低いと思われます...

于 2012-10-16T22:36:20.697 に答える
4

いいえ、今のところそうではありません (少なくとも .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) になるため、簡単に支配されます。

于 2012-10-16T22:38:01.077 に答える
1

演算子を列挙可能なシーケンスに適用する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 に一致したアイテムの数に関する情報は保持されないため、フィルター処理されたシーケンス内のアイテムの数はわかりません。

于 2012-10-16T22:44:51.920 に答える