LINQ .Any VS .Exists-차이점은 무엇입니까?


413

컬렉션에서 LINQ를 사용하면 다음 코드 줄의 차이점은 무엇입니까?

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

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

업데이트 1

분해 .Exists할 때 코드가없는 것처럼 보입니다.

업데이트 2

아무도이 코드가없는 이유를 알고 있습니까?


9
컴파일 한 코드는 어떻게 생겼습니까? 당신은 어떻게 분해 했습니까? ildasm? 무엇을 찾았지만 찾지 못했습니까?
Meinersbur

답변:


423

설명서 참조

List.Exist (오브젝트 메소드-MSDN)

List (T)에 지정된 조건 자에 의해 정의 된 조건과 일치하는 요소가 포함되어 있는지 확인합니다.

이것은 .NET 2.0 이후 LINQ 이전에 존재합니다. Predicate delegate 와 함께 사용 되지만 람다 식은 이전 버전과 호환됩니다. 또한 List만이 이것을 가지고 있습니다 (IList조차도 아닙니다)

IEnumerable.Any (확장 방법-MSDN)

시퀀스의 요소가 조건을 만족하는지 여부를 결정합니다.

이것은 .NET 3.5의 새로운 기능이며 Func (TSource, bool)를 인수로 사용하므로 람다 식 및 LINQ와 함께 사용됩니다.

행동에서, 이들은 동일합니다.


4
나중에 다른 스레드 에서 게시물 을 작성하여 .NET 2 List<>인스턴스 메소드 의 모든 Linq "등가"를 나열했습니다 .
Jeppe Stig Nielsen

201

차이점은 Any는 IEnumerable<T>System.Linq.Enumerable에 정의 된 모든 확장 방법이라는 것입니다 . 모든 IEnumerable<T>인스턴스에서 사용할 수 있습니다 .

존재하는 확장 방법이 아닌 것 같습니다. 내 생각에 콜은 유형 List<T>입니다. 그렇다면 Exists는 Any와 매우 유사한 기능을하는 인스턴스 방법입니다.

요컨대 , 방법은 본질적으로 동일합니다. 하나는 다른 것보다 더 일반적입니다.

  • 어떤은 또한 매개 변수를 사용하지 않는 단순히 열거에있는 모든 항목을 찾습니다 과부하가 있습니다.
  • 기존 과부하가 없습니다.

13
잘 넣습니다 (+1). List <T> .Exist는 .Net 2 이후로 사용되었지만 일반 목록에서만 작동합니다. IEnumerable <T>. 모든 열거 가능한 컬렉션에서 작동하는 확장으로 .Net 3에 추가되었습니다. 속성 인 List <T> .Count와 IEnumerable <T> .Count ()와 같은 비슷한 멤버도 있습니다.
Keith

51

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"

Any2의 크기만큼 느려질 것 같습니다 .

편집 : 5M 및 10M 레코드의 경우 목록을 생성하는 방식을 변경하고 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"))
            {
            }
        }
    }

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"

여기에 이미지 설명을 입력하십시오


3
당신에게 불신은 없지만, 나는이 벤치 마크에 대해 회의적인 느낌을받습니다. 숫자를보십시오 : 모든 결과에는 재귀가 발생합니다 (3770377 : 2490249). 적어도 나를 위해, 그것은 뭔가 잘못되었다는 확실한 신호입니다. 나는 여기서 수학에 대해 100 % 확신하지 못하지만 반복되는 패턴이 발생할 확률은 값 당 999 ^ 999 (또는 999! 아마도?)에서 1이라고 생각합니다. 따라서 연속으로 8 번 일어날 가능성 은 무한합니다. 벤치마킹에 DateTime을 사용하기 때문이라고 생각합니다 .
Jerri Kangasniemi

@JerriKangasniemi 같은 작업을 반복해서 반복하면 항상 같은 시간이 걸리고 여러 번 반복해야합니다. DateTime이라고 말하는 이유는 무엇입니까?
Matas Vaitkevicius

물론 그렇습니다. 문제는 여전히 500k 통화에 대해 예를 들어 0120012 초가 걸리지 않을 것입니다. 그리고 완벽하게 선형 적이므로 숫자를 잘 설명하면 1M 전화는 0240024 초가 걸렸을 것입니다 (두 배 길이). 1M 통화는 55,000보다 58, (3) % 더 오래 걸리고 10M은 5M보다 102,5 % 더 오래 걸립니다. 따라서 선형 함수가 아니므로 숫자가 모두 되풀이되는 것은 실제로 합리적이지 않습니다. DateTime은 고정밀 타이머를 사용하지 않기 때문에 과거에 스스로 문제가 발생했기 때문에 DateTime을 언급했습니다.
Jerri Kangasniemi

2
@JerriKangasniemi 문제를 해결하고 답변을 제안 해 주
시겠습니까?

1
결과를 올바르게 읽는다면 Any는 Exists 속도의 2 ~ 3 배에 불과하다고보고했습니다. 나는 데이터가 "2의 크기만큼 느리게 보일 것 같다"는 당신의 주장을 가볍게지지한다는 것을 알지 못한다. 물론, 속도가 조금 느리지는 않습니다.
Suncat2000

16

벤치마킹 에 대한 Matas의 답변 을 계속합니다 .

TL / DR : Exists ()와 Any ()는 똑같이 빠릅니다.

우선 : 스톱워치를 사용한 벤치마킹은 정확하지 않지만 ( 다른 주제 이지만 주제가 많은 series0ne의 답변 참조 ) DateTime보다 훨씬 정확합니다.

실제로 정확한 측정 값을 얻는 방법은 성능 프로파일 링을 사용하는 것입니다. 그러나 두 방법의 성능을 서로 측정하는 방법을 이해하는 한 가지 방법은 두 방법을 모두 로드 한 다음 가장 빠른 실행 시간을 비교하는 것입니다. 그 방법은, 정말 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;
    }

위의 코드를 (다시 1000을 4 번 실행 한 후 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

있습니다 약간의 차이는 있지만, 배경 잡음에 의해 설명 할 수없는 너무 작은 차이입니다. 내 생각 엔 한 10 000 100 000을한다면 있다는 것 Exists()Any()대신에, 그 약간의 차이가 다소 사라질 것이다.


이것에 대해 체계적인 방법으로 10 000과 100 000과 1000000을 할 것을 제안 할 수 있습니까? 왜 평균값이 아닌 최소값입니까?
Matas Vaitkevicius

2
최소값은 각 방법의 가장 빠른 실행 (= 아마도 가장 적은 배경 소음)을 비교하고 싶기 때문입니다. 나중에 될 것입니다하지만 내가 더 반복 함께 할 수있는 (내 상사가이 일을 대신에 우리의 백 로그를 통해 작업을 위해 저를 지불하고 싶어 의심)
제리 Kangasniemi

폴 린드버그 (Paul Lindberg)에게 물었고 그는 괜찮습니다.) 최소한 당신의 추론을 볼 수 있지만 더 많은 정통 접근 방식은 평균 en.wikipedia.org/wiki/Algorithmic_efficiency#Practice
Matas Vaitkevicius

9
게시 한 코드가 실제로 실행 한 코드 인 경우 두 측정에서 Exists를 호출 할 때 유사한 결과를 얻는 것은 놀라운 일이 아닙니다. ;)
Simon Touchtech

응, 나도 네가 그렇게 말하는 걸 봤어 내 처형은 아닙니다. 이것은 단지 내가 비교하고있는 개념을 제거 한 것입니다. : P
Jerri Kangasniemi

4

또한 Value가 bool 유형 인 경우에만 작동합니다. 일반적으로 이것은 술어와 함께 사용됩니다. 주어진 조건을 만족하는 요소가 있는지를 찾기 위해 모든 술어가 일반적으로 사용됩니다. 여기서 당신은 요소 i에서 bool 속성으로의 맵을 수행하고 있습니다. Value 속성이 true 인 "i"를 검색합니다. 완료되면 메소드는 true를 리턴합니다.


3

위에서 언급 한 바와 같이 측정을 수정하면 다음과 같은 결과가 나타납니다.

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.
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.