22

I am curious to know if these two are functionally equivalent in all cases.

Is it possible that by changing the dictionary's default comparator that these two would be functionally different?

Also, isn't Keys.Contains almost guaranteed to be slower?

user420667
  • 6,552
  • 15
  • 51
  • 83

2 Answers2

25

These two functions do exactly the same thing.

Keys.Contains exists because Keys is an ICollection<TKey>, which defines a Contains method.
The standard Dictionary<TKey, TValue>.KeyCollection implementation (the class, not the interface) defines it as

bool ICollection<TKey>.Contains(TKey item){ 
    return dictionary.ContainsKey(item); 
}

Since it's implemented explicitly, you can't even call it directly.


You're either seeing the interface, which is what I explained above, or the LINQ Contains() extension method, which will also call the native implementation since it implements ICollection<T>.

SLaks
  • 868,454
  • 176
  • 1,908
  • 1,964
  • @ReedCopsey: No, it won't be slower; I checked the source. Also, if the compile-time type of your dictionary reference is the interface, it will call `ICollection.Contains`, not the LINQ method. – SLaks Nov 23 '11 at 00:37
  • I see now that I am indeed calling the extension method. – user420667 Nov 23 '11 at 00:44
  • The only way that the LINQ method would be avoided was if you were using `IDictionary`, not `Dictionary` for your variable, though... right? – Reed Copsey Nov 23 '11 at 00:45
  • @ReedCopsey: That's exactly what I said. (Or if you explicitly cast `Keys` to `ICollection`) – SLaks Nov 23 '11 at 00:46
  • Thanks - (it wasn't clear to me that you were referring to `IDictionary`, from what you typed...) You already had my upvote, though ;) – Reed Copsey Nov 23 '11 at 00:49
  • If you are using System.Linq then myDictionary.Keys.Contains( key ) will statically bind to Enumerable.Contains which is an O(n) operation. DO NOT DO THIS!! You should always use myDictionary.ContainsKey( key ) or even better, myDictionary.TryGetValue(key, out value) – mhand Dec 11 '14 at 20:19
  • 1
    @mhand: Wrong on both counts. `Enumerable.Contains` is O(1) on `KeyCollection`, and overload resolution will prefer instance methods to extension methods. http://referencesource.microsoft.com/#System.Core/System/Linq/Enumerable.cs,1241 – SLaks Dec 11 '14 at 21:43
  • @SLaks Hmm good point, I must have overlooked the cast in the Enumerable.Contains implementation, which will end up calling ICollection.Contains and delegate properly like you stated. However, the compiler will bind to Enumerable.Contains unless it is statically known as ICollection, but it seems to be a moot point given the cast to ICollection in the Enumerable.Contains implementation – mhand Dec 12 '14 at 03:06
  • 1
    @mhand: Yes, and `IDictionary.Keys` is an `ICollection`. – SLaks Dec 12 '14 at 03:13
  • Also worth noticing that `Contains()` can accept an `IEqualityComparer` for a custom equality comparer. – Michael Haddad Feb 15 '18 at 15:57
12

Although they are pretty much equivalent for Dictionary<,>, I find it's much safer to stick with ContainsKey().

The reason is that in the future you may decide to use ConcurrentDictionary<,> (to make your code thread-safe), and in that implementation, ContainsKey is significantly faster (since accessing the Keys property does a whole bunch of locking and creates a new collection).

RobSiklos
  • 8,348
  • 5
  • 47
  • 77