2009-06-11 18 views
44

Linq Count()メソッドは、List<>.CountまたはArray.Lengthより速くまたは遅くなりますか?Linq Count()はList.CountまたはArray.Lengthより高速か、遅いのですか?

+9

最も簡単に知る方法は試してみることです。 StopWatchの適切なメソッドへの呼び出しで両方をラップし、数百万回実行すれば、わかります。 –

+1

真剣に大きなコレクションについて話している場合を除き、速度の顕著な違いはありません。読み込み/保守が容易なものだけを使用してください。 – Hardwareguy

答えて

58

一般的に遅い。 LINQのカウントは通常O(N)であり、List.CountArray.LengthはともにO(1)であることが保証されています。

ただし、IList<T>またはICollection<T>などの特定のインターフェイスタイプにキャストすると、LINQは特別な場合にIEnumerable<T>パラメータを使用します。その後、そのCountメソッドを使用して実際の操作を実行します(Count())。したがって、それはO(1)に戻るでしょう。しかし、あなたはまだキャストとインターフェイスコールのわずかなオーバーヘッドを支払うだけです。

+0

@Marc、私はそのカバエを付け加えました。 – JaredPar

+0

私はよく分かりませんが、List.Count()がIQueryable上で実行されるとselect count(*)sqlコマンドが実行されると思います。 List.Countを実行すると、すべての項目を列挙してカウントを返します。後者の場合、List.Count()はほとんどの場合、より高速になります。 – Jose

+0

@Jared、Marcsの答えはより正確です。チェックはICollection 、配列、HashSet、Dictionary、List、LinkedList、QueueのすべてがICollection を実装するためだけに行われます。古いSystem.Collectionクラスはそうではありませんが、IEnumerable を実装していないため、LINQで使用することはできません。 –

2

ICollectionまたはIList(ArrayListやListなど)のいずれかでLinq.Count()を呼び出すと、Countプロパティの値が返されると思います。したがって、パフォーマンスは単純なコレクションでほぼ同じになります。

+0

ArrayListはIEnumerable ではないため、LINQ拡張メソッドを実行することはできません。チェックはICollectionのためだけに行われます

25

.Countを用いICollection<T>ためEnumerable.Count()方法チェック、 - そうアレイ及びリストの場合には、(間接のちょうど余分レベル)はるかに非効率的ではありません。

+0

実際には、2層のインダイレクションを持つ配列では、私の答えをご覧ください:p –

2

私はそれがリストに依存していると言います。 dbのどこかのテーブルであるIQueryableの場合、Count()はより速く、すべてのオブジェクトをロードする必要がないため、より高速にになります。しかし、リストがメモリ内にあれば、Countプロパティは同じではないにしても高速になると思います。

22

マークは正しい答えを持っていますが、悪魔は詳細です。私のマシン上で

:約100倍高速.Countよりも(ある.LENGTHアレイの場合

  • リストの.Countが(.Countよりも約10倍高速である) - 注:私は希望すべての実装と同様のパフォーマンスを期待するIList<T>

.Lengthには1回の操作しか含まれていないため、配列の開始は遅くなります。ですから、アレイ上のカウントは(マシン上で)10倍遅くなります。これは、インタフェースが明示的に実装されている理由の1つになります。 2つのパブリックプロパティ、.Countと.Lengthを持つオブジェクトがあるとします。どちらもまったく同じことですが、.Countは10倍遅くなります。

もちろん、これは実際には違いがあります。なぜなら、あなたの配列を数える必要があり、パフォーマンスヒットを感じるために何百万もの時間を数えてしまうからです。

コード:

static void TimeAction(string description, int times, Action func) { 
     var watch = new Stopwatch(); 
     watch.Start(); 
     for (int i = 0; i < times; i++) { 
      func(); 
     } 
     watch.Stop(); 
     Console.Write(description); 
     Console.WriteLine(" Time Elapsed {0} ms", watch.ElapsedMilliseconds); 
    } 

    static void Main(string[] args) { 
     var array = Enumerable.Range(0, 10000000).ToArray(); 
     var list = Enumerable.Range(0, 10000000).ToArray().ToList(); 

     // jit 
     TimeAction("Ignore and jit", 1 ,() => 
     { 
      var junk = array.Length; 
      var junk2 = list.Count; 
      array.Count(); 
      list.Count(); 
     }); 


     TimeAction("Array Length", 1000000,() => { 
      var tmp1 = array.Length; 
     }); 

     TimeAction("Array Count()", 1000000,() => 
     { 
      var tmp2 = array.Count(); 
     }); 

     TimeAction("Array Length through cast", 1000000,() => 
     { 
      var tmp3 = (array as ICollection<int>).Count; 
     }); 


     TimeAction("List Count", 1000000,() => 
     { 
      var tmp1 = list.Count; 
     }); 

     TimeAction("List Count()", 1000000,() => 
     { 
      var tmp2 = list.Count(); 
     }); 

     Console.ReadKey(); 
    } 

結果:

 
Array Length Time Elapsed 3 ms 
Array Count() Time Elapsed 264 ms 
Array Length through cast Time Elapsed 16 ms 
List Count Time Elapsed 3 ms 
List Count() Time Elapsed 18 ms 
+0

'collection.Count/Length'は' collection.Count() 'よりも読みやすくなります。よりきれいなコードがより良いレアケース:P – nawfal

+0

ちょうど、私は '(ICollection ).Count;と'(ICollectionとしての配列).Count;(前者が好ましい)の間に若干の違いがあります。 – jmoreno

0

いくつかの追加情報 - LINQカウント - 巨大ことができ、それを使用していないとの違い - これは "オーバーである必要はありません。大規模なコレクションも。私はlinqから約6500個のアイテムを持つオブジェクトに結びついたコレクションを持っています(大きなものですが、巨大ではありません)。私の場合のCount()は数秒かかる。リスト(または配列)に変換すると、その数は事実上即時になります。この数を内側のループに入れることは、その影響が大きくなる可能性があることを意味します。 Countはすべてを列挙します。配列とリストは両方ともその長さを「自己認識」しており、列挙する必要はありません。このcount()を参照するすべてのデバッグ文(log4net for ex)は、すべてをかなり遅くします。あなた自身が好意を持ってこれを参照する必要がある場合は、カウントサイズを保存し、それをリストに変換してからパフォーマンスヒットなしに参照することができない限り、LINQコレクションで一度呼び出すだけです。

ここで私が上で話していたことの簡単なテストです。 Count()を呼び出すたびに、コレクションのサイズが変化するので、予想される 'count'演算以上の評価が行われます。 )注意してください:

 
    using System; 
    using System.Collections.Generic; 
    using System.Linq; 
    using System.Text; 

    namespace LinqTest 
    { 
     class TestClass 
     { 
      public TestClass() 
      { 
       CreateDate = DateTime.Now; 
      } 
      public DateTime CreateDate; 
     } 

     class Program 
     { 

      static void Main(string[] args) 
      { 
       //Populate the test class 
       List list = new List(1000); 
       for (int i=0; i<1000; i++) 
       { 
        System.Threading.Thread.Sleep(20); 
        list.Add(new TestClass()); 
        if(i%100==0) 
        { 
         Console.WriteLine(i.ToString() + " items added"); 
        } 
       } 

       //now query for items 
       var newList = list.Where(o=> o.CreateDate.AddSeconds(5)> DateTime.Now); 
       while (newList.Count() > 0) 
       { 
        //Note - are actual count keeps decreasing.. showing our 'execute' is running every time we call count. 
        Console.WriteLine(newList.Count()); 
        System.Threading.Thread.Sleep(500); 
       } 
      } 
     } 
    } 
 
+2

list.WithがCount()でショートカットを取得しないようにIEnumerableを返します...もしあなたがそれを実現したら、かなり良いperfを表示します(例えば: 'list.Where(o => o.CreateDate.AddSeconds(5)> DateTime .Now).ToList() ') –

関連する問題