web-dev-qa-db-ja.com

IEnumerable Count()と長さの違い

IEnumerableCount()Lengthの主な違いは何ですか?

58
balalakshmi

IEnumerable<T>でCountを呼び出すことで、System.Linq.Enumerableで拡張メソッドCountを参照していると仮定しています。 LengthIEnumerable<T>のメソッドではなく、int[]などの.Netの配列型のプロパティです。

違いはパフォーマンスです。 Lengthプロパティは、O(1)操作であることが保証されています。 Count拡張メソッドの複雑さは、オブジェクトのランタイムタイプによって異なります。 Countプロパティ経由でICollection<T>のようなO(1)長さルックアップをサポートするいくつかの型にキャストしようとします。利用可能なものがない場合、すべてのアイテムを列挙し、O(N)の複雑さを持つアイテムをカウントします。

例えば

int[] list = CreateSomeList();
Console.WriteLine(list.Length);  // O(1)
IEnumerable<int> e1 = list;
Console.WriteLine(e1.Count()); // O(1) 
IEnumerable<int> e2 = list.Where(x => x <> 42);
Console.WriteLine(e2.Count()); // O(N)

e2は、O(1)カウントをサポートしないC#イテレーターとして実装されているため、メソッドCountはコレクション全体を列挙して、その長さを判断する必要があります。

87
JaredPar

Jon Skeet のコメントに少し追加。

Count()拡張メソッドのソースコードは次のとおりです。

.NET 3:

public static int Count<TSource>(this IEnumerable<TSource> source)
{
    if (source == null)
    {
        throw Error.ArgumentNull("source");
    }
    ICollection<TSource> is2 = source as ICollection<TSource>;
    if (is2 != null)
    {
        return is2.Count;
    }
    int num = 0;
    using (IEnumerator<TSource> enumerator = source.GetEnumerator())
    {
        while (enumerator.MoveNext())
        {
            num++;
        }
    }
    return num;
}

.NET 4:

public static int Count<TSource>(this IEnumerable<TSource> source)
{
    if (source == null)
    {
        throw Error.ArgumentNull("source");
    }
    ICollection<TSource> is2 = source as ICollection<TSource>;
    if (is2 != null)
    {
        return is2.Count;
    }
    ICollection is3 = source as ICollection;
    if (is3 != null)
    {
        return is3.Count;
    }
    int num = 0;
    using (IEnumerator<TSource> enumerator = source.GetEnumerator())
    {
        while (enumerator.MoveNext())
        {
            num++;
        }
    }
    return num;
}
21
bniwredyc
  • 長さは固定プロパティです。一次元の配列または文字列の。したがって、カウント操作は必要ありません(多次元配列にはすべての次元のサイズが乗算されています)。 O(1)ここでの操作は、要素の数に関係なく、検索時間が常に同じであることを意味します。線形検索(これに反対)はO(n)になります。

  • ICollectionsのCountプロパティ(たとえば、ListおよびList <T>)は変更される可能性があるため、追加/削除操作で更新するか、コレクションの変更後にCountが要求されたときに更新する必要があります。オブジェクトの実装に依存します。

  • LINQのCount()メソッドは、基本的に、呼び出されるたびに繰り返されます(オブジェクトがICollection型である場合を除き、ICollection.Countプロパティが要求されます)。

IEnumerablesは、多くの場合、既に定義されているオブジェクトコレクション(リスト、配列、ハッシュテーブルなど)ではなく、バックグラウンド操作にリンクし、要求されるたびに結果を生成します(遅延実行と呼ばれます)。

通常、次のようなLINQステートメントのようなSQLがあります(遅延実行の典型的なアプリケーション):

IEnumerable<Person> deptLeaders = 
   from p in persons
   join d in departments
      on p.ID equals d.LeaderID
   orderby p.LastName, p.FirstName
   select p;

次に、次のようなコードがあります。

if (deptLeaders.Count() > 0)
{
   ReportNumberOfDeptLeaders(deptLeaders.Count());
   if (deptLeaders.Count() > 20)
      WarnTooManyDepartmentLeaders(deptLeaders.Count());
}

したがって、部門リーダーの数が多すぎるという警告が発行されると、.NETは人を4回調べ、部門リーダーと照合し、名前で並べ替えてから結果オブジェクトをカウントします。

そして、これは個人と部門が事前に設定された値のコレクションであり、クエリ自体ではない場合のみです。

1
Erik Hart