回答:
ドキュメントを見る
List.Exists(Objectメソッド-MSDN)
List(T)に、指定された述語で定義された条件に一致する要素が含まれているかどうかを判断します。
これは.NET 2.0以降、つまりLINQ以前に存在します。述語デリゲートと共に使用することを意味しますが、ラムダ式には下位互換性があります。また、Listだけがこれを持っています(IListも含みません)
IEnumerable.Any(拡張メソッド-MSDN)
シーケンスの要素が条件を満たすかどうかを決定します。
これは.NET 3.5の新機能であり、引数としてFunc(TSource、bool)を使用するため、ラムダ式およびLINQで使用することを目的としていました。
動作では、これらは同じです。
違いは、AnyはIEnumerable<T>
System.Linq.Enumerableで定義されたすべての拡張メソッドであることです。任意のIEnumerable<T>
インスタンスで使用できます。
Existsは拡張メソッドではないようです。私の推測では、collはタイプList<T>
です。存在する場合、ExistsはAnyと非常によく機能するインスタンスメソッドです。
つまり、メソッドは基本的に同じです。一方は他方より一般的です。
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;
}
1,000万件のレコード
「すべて:00:00:00.3770377存在:00:00:00.2490249」
5Mレコード
「すべて:00:00:00.0940094存在:00:00:00.1420142」
100万件のレコード
「すべて:00:00:00.0180018存在:00:00:00.0090009」
500kの場合(私はまた、最初に実行されたものに関連付けられている追加の操作がないかどうかを確認するために評価される順序を反転しました。)
"存在:00:00:00.0050005任意:00:00:00.0100010"
10万件のレコード
「存在:00:00:00.0010001任意:00:00:00.0020002」
Any
速度は2倍遅くなるようです。
編集: 500万レコードと1000万レコードの場合、リストの生成方法を変更したところ、Exists
突然Any
テストが遅くなり、テスト方法に問題があることがわかりました。
新しいテストメカニズム:
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:わかりました。テストデータの生成による影響を排除するために、すべてをファイルに書き込んで、そこから読み取ります。
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"))
{
}
}
}
1,000万
「すべて:00:00:00.1640164存在:00:00:00.0750075」
500万
「すべて:00:00:00.0810081存在:00:00:00.0360036」
100万
「すべて:00:00:00.0190019存在:00:00:00.0070007」
50万
「すべて:00:00:00.0120012存在:00:00:00.0040004」
ベンチマークに関するMatasの回答の続きとして。
TL / DR:Exists()とAny()は同等に高速です。
まず、ストップウォッチを使用したベンチマークは正確ではありませんが(series0neの別の回答を参照してください。ただし、トピックは類似しています)、DateTimeよりもはるかに正確です。
本当に正確な測定値を取得する方法は、パフォーマンスプロファイリングを使用することです。しかし、2つのメソッドのパフォーマンスが互いにどの程度合っているかを理解する1つの方法は、両方のメソッドをロードして実行し、それぞれの最速の実行時間を比較することです。そうすれば、それは本当にJITingやその他のノイズは私たちに悪い測定値を与える(とそれがいることを問題ではないん両方の実行が「されているので、)均等misguiding意味で」。
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 Exists()
を実行Any()
し、1 000 000の要素を持つリストで)、メソッドがほぼ同じくらい高速であることを確認するのは難しくありません。
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
わずかな違いがありますが、バックグラウンドノイズでは説明できないほど小さい違いです。私の推測では、1万人または10万人Exists()
とAny()
すると、そのわずかな差は多かれ少なかれ解消されるでしょう。
上記のように測定値を修正すると、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.