20

LINQ 拡張メソッドを先頭に追加するか追加するかは明らかに問題ではないことに驚いています。

テスト済みEnumerable.FirstOrDefault:

  1. hugeList.Where(x => x.Text.Contains("10000")).FirstOrDefault();
  2. hugeList.FirstOrDefault(x => x.Text.Contains("10000"));

    var hugeList = Enumerable.Range(1, 50000000)
        .Select(i => new { ID = i, Text = "Item" + i });
    
    var sw1 = new System.Diagnostics.Stopwatch();
    var sw2 = new System.Diagnostics.Stopwatch();
    
    sw1.Start();
    for(int i=0;i<1000;i++)
        hugeList.Where(x => x.Text.Contains("10000")).FirstOrDefault();
    sw1.Stop();
    
    sw2.Start();
    for(int i=0;i<1000;i++)
        hugeList.FirstOrDefault(x => x.Text.Contains("10000"));
    sw2.Stop();
    
    var result1 = String.Format("FirstOrDefault after: {0} FirstOrDefault before: {1}", sw1.Elapsed,  sw2.Elapsed);
    //result1: FirstOrDefault after: 00:00:03.3169683 FirstOrDefault before: 00:00:03.0463219
    
    sw2.Restart();
    for (int i = 0; i < 1000; i++)
        hugeList.FirstOrDefault(x => x.Text.Contains("10000"));
    sw2.Stop();
    
    sw1.Restart();
    for (int i = 0; i < 1000; i++)
        hugeList.Where(x => x.Text.Contains("10000")).FirstOrDefault();
    sw1.Stop();
    
    var result2 = String.Format("FirstOrDefault before: {0} FirstOrDefault after: {1}", sw2.Elapsed, sw1.Elapsed);
    //result2: FirstOrDefault before: 00:00:03.6833079 FirstOrDefault after: 00:00:03.1675611
    
    //average after:3.2422647 before: 3.3648149 (all seconds)
    

Where一致するすべてのアイテムを見つけてから最初のものを取得する必要があり、先行するとFirstOrDefault最初に見つかったアイテムが生成される可能性があるため、先頭に追加するのは遅くなると思いました。

Q:なぜ私が間違った道を歩んでいるのか誰か説明してもらえますか?

4

2 に答える 2

49

一致するすべてのアイテムを見つけてから最初のものを取得する必要があり、先行する FirstOrDefault が最初に見つかったアイテムを生成する可能性があるため、Where を前に追加するのは遅いと思いました。誰かがなぜ私が間違った道を進んでいるのか説明できますか?

最初のステートメントが単に間違っているため、間違った方向に進んでいます。最初に一致するアイテムを取得する前に、一致するすべてのアイテムを見つける必要Whereはありません。「オンデマンド」で一致するアイテムを取得します。最初のものだけを要求すると、最初のものだけが取得されます。最初の 2 つだけを要求すると、最初の 2 つだけが取得されます。Where

ジョン・スキートがステージでいい感じです。3 人いるとします。最初の人はシャッフルされたカードのパックを持っています。2 番目の人は、"where card is red" と書かれた T シャツを持っています。3 人目が 2 人目をつつき、「最初のカードをくれ」と言います。2 番目の人は、1 番目の人がレッドカードを渡すまで、1番目の人を何度も突く。2 番目の人が最初の人をつつき続ける理由はありません。タスクは完了です!

ここで、2 人目の T シャツに「昇順順」と表示されている場合は、状況が大きく異なります。2 番目の人は、最初の人からすべてのカードを取得する必要があります。これは、最初のカードを 3 番目の人に渡す前に、デッキの一番下のカードを見つけるためです。

これにより、パフォーマンス上の理由で順序が重要になる場合を判断するために必要な直感が得られるはずです。「赤いカードを渡してから並べ替える」の最終的な結果は、「すべてのカードを並べ替えてから赤いカードを渡す」とまったく同じですが、前者は並べ替えに時間を費やす必要がないため、はるかに高速です。あなたが捨てようとしている黒いカード。

于 2012-04-11T16:42:22.640 に答える
11

このメソッドは遅延実行を使用し、要求されたときWhere()に次に一致するアイテムを提供します。つまり、すべての候補オブジェクトのシーケンスを評価してすぐに返すのではなく、反復処理中に一度に 1 つずつ提供します。Where()

FirstOrDefault()最初のアイテムの後で停止するため、これにより、反復Where()も停止します。

を実行したかのようにFirstOrDefault()の実行を停止すると考えてください。もちろん、それほど単純ではありませんが、本質的には、アイテムが見つかったら反復を停止するため、それ以上先に進む必要はありません。Where()breakFirstOrDefault()Where()

もちろん、これは句FirstOrDefault()に aを適用する単純なケースですWhere()。すべての項目を考慮する必要があることを暗示する他の句がある場合、これは影響を与える可能性がありますが、これはWhere().FirstOrDefault()' combo or justFirstOrDefault()を使用する場合にも当てはまります。述語。

于 2012-04-11T16:31:44.677 に答える