Dictionary Keys.Contains vs. ContainsKey: эквивалентны ли они функционально?

Мне любопытно узнать, являются ли эти два функционально эквивалентными во всех случаях.

Возможно ли, что, изменив компаратор словаря по умолчанию, эти два будут функционально разными?

Кроме того, разве Keys.Contains почти гарантированно не будет медленнее?


person user420667    schedule 23.11.2011    source источник


Ответы (2)


Эти две функции делают одно и то же.

Keys.Contains существует, потому что Keys является ICollection<TKey>, который определяет метод Contains.
Стандартная реализация Dictionary<TKey, TValue>.KeyCollection (класс, а не интерфейс) определяет его как

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

Поскольку он реализован явно, вы даже не можете вызвать его напрямую.


Вы либо видите интерфейс, как я объяснял выше, либо метод расширения LINQ Contains(), который также вызывает нативную реализацию, поскольку реализует ICollection<T>.

person SLaks    schedule 23.11.2011
comment
@ReedCopsey: Нет, медленнее не будет; Я проверил источник. Кроме того, если типом времени компиляции вашей ссылки на словарь является интерфейс, он будет вызывать ICollection<TKey>.Contains, а не метод LINQ. - person SLaks; 23.11.2011
comment
Теперь я вижу, что действительно вызываю метод расширения. - person user420667; 23.11.2011
comment
Единственный способ избежать использования метода LINQ — это использовать IDictionary<T,U>, а не Dictionary<T,U> для своей переменной, хотя... верно? - person Reed Copsey; 23.11.2011
comment
@ReedCopsey: именно это я и сказал. (Или если вы явно приводите Keys к ICollection<TKey>) - person SLaks; 23.11.2011
comment
Спасибо - (мне не было ясно, что вы имели в виду IDictionary<T,U>, из того, что вы напечатали...) Однако вы уже получили мой голос;) - person Reed Copsey; 23.11.2011
comment
Если вы используете System.Linq, то myDictionary.Keys.Contains(key) будет статически связываться с Enumerable.Contains, что является операцией O(n). НЕ ДЕЛАЙ ЭТО!! Вы всегда должны использовать myDictionary.ContainsKey(key) или даже лучше, myDictionary.TryGetValue(key, out value) - person mhand; 11.12.2014
comment
@mhand: неправильно в обоих случаях. Enumerable.Contains равен O(1) на KeyCollection, и разрешение перегрузки предпочтет методы экземпляра методам расширения. referencesource.microsoft.com/#System.Core/System/ Линк/ - person SLaks; 12.12.2014
comment
@SLaks Хм, хорошая мысль, я, должно быть, упустил из виду приведение в реализации Enumerable.Contains, которая в конечном итоге вызовет ICollection‹TKey›.Contains и делегирует правильно, как вы сказали. Однако компилятор будет связываться с Enumerable.Contains, если только он не известен статически как ICollection‹TKey›, но это кажется спорным, учитывая приведение к ICollection‹TKey› в реализации Enumerable.Contains - person mhand; 12.12.2014
comment
@mhand: Да, и IDictionary<K, V>.Keys это ICollection<K>. - person SLaks; 12.12.2014
comment
Также стоит отметить, что Contains() может принимать IEqualityComparer<T> для пользовательского компаратора равенства. - person Sipo; 15.02.2018

Хотя они в значительной степени эквивалентны для Dictionary<,>, я считаю, что гораздо безопаснее придерживаться ContainsKey().

Причина в том, что в будущем вы можете решить использовать ConcurrentDictionary<,> (чтобы сделать ваш код потокобезопасным), и в этой реализации ContainsKey значительно быстрее (поскольку доступ к свойству Keys выполняет целую кучу блокировок и создает новую коллекцию). .

person RobSiklos    schedule 24.06.2014