8

コレクションが空であってはならず、アイテムが 1 つしか含まれていないことを知る必要があるコードがあります。

一般に、次の形式の拡張が必要です。

bool collectionHasAtLeast2Items = collection.AtLeast(2);

コレクションを列挙し、要求されたサイズに達するか、要素がなくなるまでインデクサーをインクリメントする拡張機能を簡単に作成できますが、これを行う LINQ フレームワークに既に何かありますか? 私の考え(思いついた順)は次のとおりです::

bool collectionHasAtLeast2Items = collection.Take(2).Count() == 2;また

bool collectionHasAtLeast2Items = collection.Take(2).ToList().Count == 2;

コレクションに含まれる要素よりも多くの要素を取得する動作は定義されていませんが (ドキュメントでは) Enumerable.Take Methodで動作するように見えますが、期待どおりの動作をするようです。

要素を取得するために一度列挙してから、要素を取得するためにもう一度列挙するか、または要素を取得するために一度列挙してから、列挙子ではない count プロパティを取得するためにリストを構築するのは、最も効率的なソリューションではありません。 -y、実際にはリストが必要ないため。

最初に「x」を取得し、次に実際に「x」を受け取ったことを確認するという 2 つのアサーションを常に行わなければならないため、きれいではありません。これは、文書化されていない動作に依存します。

または、おそらく次を使用できます。

bool collectionHasAtLeast2Items = collection.ElementAtOrDefault(2) != null;

ただし、それは意味的に明確ではありません。おそらく、私が望むものを意味するメソッド名でそれをラップするのが最善です。これは効率的だと思いますが、コードには反映していません。

を使用している他の考えもありますがLast()、コレクション全体を列挙したくありません。

それともSkip(2).Any()、意味的に完全に明らかではありませんが、 よりも優れてElementAtOrDefault(2) != nullいますが、同じ結果が得られると思いますか?

何かご意見は?

4

3 に答える 3

4
public static bool AtLeast<T>(this IEnumerable<T> source, int count)
{
    // Optimization for ICollection<T>
    var genericCollection = source as ICollection<T>;
    if (genericCollection != null)
        return genericCollection.Count >= count;

    // Optimization for ICollection
    var collection = source as ICollection;
    if (collection != null)
        return collection.Count >= count;

    // General case
    using (var en = source.GetEnumerator())
    {
        int n = 0;
        while (n < count && en.MoveNext()) n++;
        return n == count;
    }
}
于 2012-05-14T12:31:16.910 に答える
4

Count() >= 2実装をシーケンスする場合、使用できますICollectionか?


舞台裏では、Enumerable.Count()拡張メソッドのチェックがループの実装の下でシーケンスを実行しICollectionます。実際にそうであれば、Countプロパティが返されるため、目標パフォーマンスは O(1) になるはずです。

したがって((IEnumerable<T>)((ICollection)sequence)).Count() >= x、O(1) も必要です。

于 2012-05-14T12:16:47.917 に答える
3

を使用することもできますCountが、パフォーマンスが問題になる場合は、を使用することをお勧めしますTake

bool atLeastX = collection.Take(x).Count() == x;

Take(私は)延期された実行を使用するので、コレクションを通過するのは1回だけです。

abatishchevは、それCountがO(1)であると述べたICollectionので、このようなことをして、両方の世界を最大限に活用することができます。

IEnumerable<int> col;
// set col
int x;
// set x
bool atLeastX;
if (col is ICollection<int>)
{
    atLeastX = col.Count() >= x;
}
else
{
    atLeastX = col.Take(x).Count() == x;
}

を使用することもできますがSkip/Any、実際には、よりもさらに高速になると思いTake/Countます。

于 2012-05-14T12:27:48.877 に答える