IEnumerable
Count()
とLength
の主な違いは何ですか?
IEnumerable<T>
でCountを呼び出すことで、System.Linq.Enumerable
で拡張メソッドCount
を参照していると仮定しています。 Length
はIEnumerable<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
はコレクション全体を列挙して、その長さを判断する必要があります。
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;
}
長さは固定プロパティです。一次元の配列または文字列の。したがって、カウント操作は必要ありません(多次元配列にはすべての次元のサイズが乗算されています)。 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回調べ、部門リーダーと照合し、名前で並べ替えてから結果オブジェクトをカウントします。
そして、これは個人と部門が事前に設定された値のコレクションであり、クエリ自体ではない場合のみです。