web-dev-qa-db-ja.com

Linq .Any VS.Exists - 違いは何ですか?

コレクションにLinqを使って、次のコード行の違いは何ですか?

if(!coll.Any(i => i.Value))

そして

if(!coll.Exists(i => i.Value))

アップデート1

.Existsを逆アセンブルすると、コードがないように見えます。

アップデート2

誰もがなぜこれにコードがないのか知っていますか?

375
Anthony D

ドキュメンテーションを見る

List.Exists (オブジェクトメソッド - MSDN)

指定された述語によって定義された条件に一致する要素がList(T)に含まれるかどうかを決定します。

これは.NET 2.0以降、LINQの前から存在します。 Predicateデリゲートと共に使用することを意味しますが、ラムダ式は下位互換性があります。また、ちょうどリストはこれを持っています(IListさえも)

IEnumerable.Any (拡張メソッド - MSDN)

シーケンスのいずれかの要素が条件を満たすかどうかを判断します。

これは.NET 3.5で新たに導入されたもので、引数としてFunc(TSource、bool)を使っていますので、これはラムダ式とLINQで使われることを意図していました。

動作上、これらは同一です。

381
Meinersbur

違いは、AnyはSystem.Linq.Enumerableで定義されているすべてのIEnumerable<T>の拡張メソッドです。どのIEnumerable<T>インスタンスでも使用できます。

Existsは拡張方法ではないようです。私の推測では、collはList<T>型です。もしそうなら、ExistsはAnyと非常によく似た機能を持つインスタンスメソッドです。

手短に言えばメソッドは基本的に同じです。一方が他方よりも一般的です

  • Anyにもパラメータをとらずに単純にenumerable内の項目を探すオーバーロードがあります。
  • Existsはそのような過負荷を持ちません。
187
JaredPar

TLDR;パフォーマンス的にAnyは遅くなりそうです(両方の値をほぼ同時に評価するようにこれを正しく設定している場合) )

        var list1 = Generate(1000000);
        var forceListEval = list1.SingleOrDefault(o => o == "0123456789012");
        if (forceListEval != "sdsdf")
        {
            var s = string.Empty;
            var start2 = DateTime.Now;
            if (!list1.Exists(o => o == "0123456789012"))
            {
                var end2 = DateTime.Now;
                s += " Exists: " + end2.Subtract(start2);
            }

            var start1 = DateTime.Now;
            if (!list1.Any(o => o == "0123456789012"))
            {
                var end1 = DateTime.Now;
                s +=" Any: " +end1.Subtract(start1);
            }

            if (!s.Contains("sdfsd"))
            {

            }

テストリストジェネレータ

private List<string> Generate(int count)
    {
        var list = new List<string>();
        for (int i = 0; i < count; i++)
        {
            list.Add( new string(
            Enumerable.Repeat("ABCDEFGHIJKLMNOPQRSTUVWXYZ", 13)
                .Select(s =>
                {
                    var cryptoResult = new byte[4];
                    new RNGCryptoServiceProvider().GetBytes(cryptoResult);
                    return s[new Random(BitConverter.ToInt32(cryptoResult, 0)).Next(s.Length)];
                })
                .ToArray())); 
        }

        return list;
    }

10Mレコードあり

"すべて:00:00:00.3770377存在:00:00:00.2490249"

5Mレコードあり

"任意:00:00:00.0940094が存在します:00:00:00.1420142"

1Mレコードあり

"任意:00:00:00.0180018が存在します:00:00:00.0090009"

500kでは、(どちらを先に実行しても、追加の操作がないかどうかを確認するために評価される順序を逆にしました。)

":00:00:00.0050005すべて:00:00:00.0100010"

100kレコードあり

"存在:00:00:00.0010001任意:00:00:00.0020002"

Anyは2倍遅くなるようです。

編集:5および10Mレコードの場合、リストの生成方法を変更したところ、ExistsAnyよりも遅くなりました。これは、テスト方法に問題があることを意味します。

新しいテストメカニズム

private static IEnumerable<string> Generate(int count)
    {
        var cripto = new RNGCryptoServiceProvider();
        Func<string> getString = () => new string(
            Enumerable.Repeat("ABCDEFGHIJKLMNOPQRSTUVWXYZ", 13)
                .Select(s =>
                {
                    var cryptoResult = new byte[4];
                    cripto.GetBytes(cryptoResult);
                    return s[new Random(BitConverter.ToInt32(cryptoResult, 0)).Next(s.Length)];
                })
                .ToArray());

        var list = new ConcurrentBag<string>();
        var x = Parallel.For(0, count, o => list.Add(getString()));
        return list;
    }

    private static void Test()
    {
        var list = Generate(10000000);
        var list1 = list.ToList();
        var forceListEval = list1.SingleOrDefault(o => o == "0123456789012");
        if (forceListEval != "sdsdf")
        {
            var s = string.Empty;

            var start1 = DateTime.Now;
            if (!list1.Any(o => o == "0123456789012"))
            {
                var end1 = DateTime.Now;
                s += " Any: " + end1.Subtract(start1);
            }

            var start2 = DateTime.Now;
            if (!list1.Exists(o => o == "0123456789012"))
            {
                var end2 = DateTime.Now;
                s += " Exists: " + end2.Subtract(start2);
            }

            if (!s.Contains("sdfsd"))
            {

            }
        }

Edit2:Okテストデータの生成による影響を排除するために、すべてファイルに書き込み、そこから読み取ります。

 private static void Test()
    {
        var list1 = File.ReadAllLines("test.txt").Take(500000).ToList();
        var forceListEval = list1.SingleOrDefault(o => o == "0123456789012");
        if (forceListEval != "sdsdf")
        {
            var s = string.Empty;
            var start1 = DateTime.Now;
            if (!list1.Any(o => o == "0123456789012"))
            {
                var end1 = DateTime.Now;
                s += " Any: " + end1.Subtract(start1);
            }

            var start2 = DateTime.Now;
            if (!list1.Exists(o => o == "0123456789012"))
            {
                var end2 = DateTime.Now;
                s += " Exists: " + end2.Subtract(start2);
            }

            if (!s.Contains("sdfsd"))
            {
            }
        }
    }

10M

"すべて:00:00:00.1640164存在:00:00:00.0750075"

5M

"すべて:00:00:00.0810081存在:00:00:00.0360036"

1M

"すべて:00:00:00.0190019存在:00:00:00.0070007"

500k

"任意:00:00:00.0120012が存在します:00:00:00.0040004"

enter image description here

47

ベンチマークに関する Matasの回答 の続きとして。

TL/DR:Exists()とAny()は同じくらい速いです。

最初に:ストップウォッチを使ったベンチマークは正確ではありません( 別の、しかしよく似たトピックについてのseries0neの答えを見てください )、それはDateTimeよりはるかに正確です。

本当に正確な測定値を取得する方法は、パフォーマンスプロファイリングを使用することです。しかし、2つのメソッドのパフォーマンスがお互いにどの程度一致しているかを理解するための1つの方法は、両方のメソッドをloads回)実行し、次にそれぞれの最速実行時間を比較することです。 JITingやその他のノイズが私たちに悪い測定値を与えることは問題ではありません(そしてそれはdoes)です。なぜなら両方の実行はある意味で "同様に誤解)"です。

static void Main(string[] args)
    {
        Console.WriteLine("Generating list...");
        List<string> list = GenerateTestList(1000000);
        var s = string.Empty;

        Stopwatch sw;
        Stopwatch sw2;
        List<long> existsTimes = new List<long>();
        List<long> anyTimes = new List<long>();

        Console.WriteLine("Executing...");
        for (int j = 0; j < 1000; j++)
        {
            sw = Stopwatch.StartNew();
            if (!list.Exists(o => o == "0123456789012"))
            {
                sw.Stop();
                existsTimes.Add(sw.ElapsedTicks);
            }
        }

        for (int j = 0; j < 1000; j++)
        {
            sw2 = Stopwatch.StartNew();
            if (!list.Exists(o => o == "0123456789012"))
            {
                sw2.Stop();
                anyTimes.Add(sw2.ElapsedTicks);
            }
        }

        long existsFastest = existsTimes.Min();
        long anyFastest = anyTimes.Min();

        Console.WriteLine(string.Format("Fastest Exists() execution: {0} ticks\nFastest Any() execution: {1} ticks", existsFastest.ToString(), anyFastest.ToString()));
        Console.WriteLine("Benchmark finished. Press any key.");
        Console.ReadKey();
    }

    public static List<string> GenerateTestList(int count)
    {
        var list = new List<string>();
        for (int i = 0; i < count; i++)
        {
            Random r = new Random();
            int it = r.Next(0, 100);
            list.Add(new string('s', it));
        }
        return list;
    }

上記のコードを4回実行した後(順番に1 000 000の要素を持つリストに対して1 000 Exists()Any()を実行します)、メソッドがほぼ同等に高速であることを確認するのは難しくありません。

Fastest Exists() execution: 57881 ticks
Fastest Any() execution: 58272 ticks

Fastest Exists() execution: 58133 ticks
Fastest Any() execution: 58063 ticks

Fastest Exists() execution: 58482 ticks
Fastest Any() execution: 58982 ticks

Fastest Exists() execution: 57121 ticks
Fastest Any() execution: 57317 ticks

そこにisわずかな違いがありますが、バックグラウンドノイズでは説明できないほど小さすぎる違いです。代わりに10 000または100 000 Exists()Any()を実行すると、そのわずかな違いがさらに消えます。もっと少なく。

14

あなたが測定値を修正するとき - 上で述べたように:AnyとExists、そして平均値を加える - 我々は以下の出力を得るでしょう:

Executing search Exists() 1000 times ... 
Average Exists(): 35566,023
Fastest Exists() execution: 32226 

Executing search Any() 1000 times ... 
Average Any(): 58852,435
Fastest Any() execution: 52269 ticks

Benchmark finished. Press any key.
4
jasmintmp

さらに、これはValueがbool型の場合にのみ機能します。通常、これは述語と共に使用されます。与えられた条件を満たす要素があるかどうかを見出すために、どんな述語も一般的に使われます。ここでは、要素iからboolプロパティへのマップを作成しています。 Valueプロパティがtrueである "i"を検索します。完了すると、メソッドはtrueを返します。

4
flq