Containskey VS Try Catch

2019-04-29 12:36发布

I have a list of Vector2's Generated I have to check against a dictionary to see if they exist, this function gets executed every tick.

which would run fastest/ be better to do it this way?

    public static bool exists(Vector2 Position, Dictionary<Vector2, object> ToCheck)
    {
        try
        {
            object Test = ToCheck[Position];
            return (true);
        }
        catch 
        {
            return (false);
        }           
    }

Or should I stick with The norm ?

    public static bool exists(Vector2 Position, Dictionary<Vector2, object> ToCheck)
    {
        if (ToCheck.ContainsKey(Position))
        {
            return (true);
        }
        return (false);
    }

Thanks for the input :)

Side Note: (The Value for the key doesn't matter at this point or i would use TryGetValue instead of ContainsKey)

4条回答
乱世女痞
2楼-- · 2019-04-29 13:12

I know it's an old question, but just to add a bit of empirical data...

Running 50,000,000 look-ups on a dictionary with 10,000 entries and comparing relative times to complete:

..if every look-up is successful:

  • a straight (unchecked) run takes 1.2 seconds
  • a guarded (ContainsKey) run takes 2 seconds
  • a handled (try-catch) run takes 1.21 seconds

..if 1 out of every 10,000 look-ups fail:

  • a guarded (ContainsKey) run takes 2 seconds
  • a handled (try-catch) run takes 1.37 seconds

..if 16 out of every 10,000 look-ups fail:

  • a guarded (ContainsKey) run takes 2 seconds
  • a handled (try-catch) run takes 3.27 seconds

..if 250 out of every 10,000 look-ups fail:

  • a guarded (ContainsKey) run takes 2 seconds
  • a handled (try-catch) run takes 32 seconds

..so a guarded test will add a constant overhead and nothing more, and try-catch test will operate almost as fast as no test if it never fails, but kills performance proportionally to the number of failures.

Code I used to run tests:

using System;
using System.Collections.Generic;

namespace ConsoleApplication1
{
   class Program
   {
      static void Main(string[] args)
      {  Test(0);
         Test(1);
         Test(16);
         Test(250);
      }

      private static void Test(int failsPerSet)
      {  Dictionary<int, bool> items = new Dictionary<int,bool>();

         for(int i =  0; i < 10000; i++)
            if(i >= failsPerSet)
               items[i] = true;

         if(failsPerSet == 0)
            RawLookup(items, failsPerSet);

         GuardedLookup(items, failsPerSet);

         CaughtLookup(items, failsPerSet);

      }

      private static void RawLookup
      (  Dictionary<int, bool> items
      ,  int             failsPerSet
      ){ int                   found = 0;
         DateTime              start ;

         Console.Write("Raw     (");
         Console.Write(failsPerSet);
         Console.Write("): ");

         start = DateTime.Now;
         for(int i = 0; i < 50000000; i++)
         {  int pick = i % 10000;
            if(items[pick])
               found++;
         }

         Console.WriteLine(DateTime.Now - start);
      }

      private static void GuardedLookup
      (  Dictionary<int, bool> items
      ,  int             failsPerSet
      ){ int                   found = 0;
         DateTime              start ;

         Console.Write("Guarded (");
         Console.Write(failsPerSet);
         Console.Write("): ");

         start = DateTime.Now;
         for(int i = 0; i < 50000000; i++)
         {  int pick = i % 10000;
            if(items.ContainsKey(pick))
               if(items[pick])
                  found++;
         }

         Console.WriteLine(DateTime.Now - start);
      }

      private static void CaughtLookup
      (  Dictionary<int, bool> items
      ,  int             failsPerSet
      ){ int                   found = 0;
         DateTime              start ;

         Console.Write("Caught  (");
         Console.Write(failsPerSet);
         Console.Write("): ");

         start = DateTime.Now;
         for(int i = 0; i < 50000000; i++)
         {  int pick = i % 10000;
            try
            {  if(items[pick])
                  found++;
            }
            catch
            {  
            }
         }

         Console.WriteLine(DateTime.Now - start);
      }

   }
}
查看更多
贪生不怕死
3楼-- · 2019-04-29 13:17

Side Note: (The Value for the key doesn't matter at this point or i would use TryGetValue instead of ContainsKey)

The answer you accepted is correct, but just to add, if you only care about the key and not the value, maybe you're looking for a HashSet rather than a Dictionary?

In addition, your second code snippet is a method which literally adds zero value. Just use ToCheck.ContainsKey(Position), don't make a method which just calls that method and returns its value but does nothing else.

查看更多
我只想做你的唯一
4楼-- · 2019-04-29 13:31

Definitely use the ContainsKey check; exception handling can add a large overhead.

Throwing exceptions can negatively impact performance. For code that routinely fails, you can use design patterns to minimize performance issues.

Exceptions are not meant to be used for conditions you can check for.

I recommend reading the MSDN documentation on exceptions generally, and on exception handling in particular.

查看更多
Melony?
5楼-- · 2019-04-29 13:32

Never use try/catch as a part of your regular program path. It is really expensive and should only catch errors that you cannot prevent. ContainsKey is the way to go here.

Side Note: No. You would not. If the value matters you check with ContainsKey if it exists and retrieve it, if it does. Not try/catch.

查看更多
登录 后发表回答