Из записи MSDN о методе Dictionary.TryGetValue :
Этот метод объединяет функциональность метода ContainsKey и свойства Item.
Если ключ не найден, параметр value получает соответствующее значение по умолчанию для типа значения TValue; например, 0 (ноль) для целочисленных типов, false для логических типов и null для ссылочных типов.
Используйте метод TryGetValue, если ваш код часто пытается получить доступ к ключам, которых нет в словаре. Использование этого метода более эффективно, чем перехват KeyNotFoundException, генерируемого свойством Item.
Этот метод приближается к операции O (1).
Из описания не ясно, является ли это более эффективным или просто более удобным, чем вызов ContainsKey и последующий поиск. Осуществляет ли реализация TryGetValue
просто вызов ContainsKey, а затем Item или на самом деле более эффективна, чем выполнение одного поиска?
Другими словами, что является более эффективным (то есть, который выполняет меньше поисков):
Dictionary<int,int> dict;
//...//
int ival;
if(dict.ContainsKey(ikey))
{
ival = dict[ikey];
}
else
{
ival = default(int);
}
или
Dictionary<int,int> dict;
//...//
int ival;
dict.TryGetValue(ikey, out ival);
Примечание: я не ищу эталон!
источник
if(dict.ContainsKey(ikey)) dict[ikey]++; else dict.Add(ikey, 0);
. Но я думаю, чтоTryGetValue
это все еще более эффективно, так как используются get и set свойства indexer, не так ли?Быстрый тест показывает, что
TryGetValue
имеет небольшое преимущество:Это производит
что делает
ContainsKey + Item
доступ около 40% медленнее , предполагая даже сочетание попаданий и промахов.Более того, когда я изменяю программу так, чтобы она всегда пропускала (то есть всегда смотрела вверх
"b"
), обе версии стали одинаково быстрыми:Когда я делаю это "все хиты", однако,
TryGetValue
остается явным победителем:источник
TryGetValue
должны быть далеко впереди. Кроме того ... придиркаDateTime
- это не лучший способ измерения производительности.TryGetValue
идете еще дальше в лидеры. Я отредактировал ответ, включив в него сценарии «все попадания» и «все пропуски».Any
- Как это:Any(i=>i.Key==key)
. В таком случае, да, это плохой линейный поиск в словаре.DateTime.Now
будет только с точностью до нескольких мс. ИспользуйтеStopwatch
класс вSystem.Diagnostics
вместо (который использует QueryPerformanceCounter под одеялом , чтобы обеспечить более высокую точность). Это также проще в использовании.Поскольку ни один из ответов до сих пор фактически не отвечает на вопрос, вот приемлемый ответ, который я нашел после некоторого исследования:
Если вы декомпилируете TryGetValue, вы увидите, что он делает это:
тогда как метод ContainsKey:
поэтому TryGetValue - это просто ContainsKey плюс поиск в массиве, если элемент присутствует.
Источник
Похоже, что TryGetValue будет почти в два раза быстрее, чем комбинация ContainsKey + Item.
источник
Какая разница :-)
Вы, вероятно, спрашиваете, потому что
TryGetValue
это боль в использовании - поэтому заключите это в капсулу с помощью метода расширения.Тогда просто позвоните:
или
источник
this
но оказывается, что мой метод дублировался дважды в моей базе кода - один раз и один безthis
, поэтому я никогда не ловил его! спасибо за исправление!TryGetValue
присваивает значение по умолчанию параметру out value, если ключ не существует, поэтому это можно упростить.if(!dic.TryGetValue(key, out value item)) item = dic[key] = new Item();
Почему бы тебе не проверить это?
Но я уверен, что
TryGetValue
это быстрее, потому что это только один поиск. Конечно, это не гарантируется, то есть разные реализации могут иметь разные характеристики производительности.Я бы реализовал словарь, создав внутреннюю
Find
функцию, которая находит слот для элемента, а затем строит остальное поверх этого.источник
Все ответы до сих пор, хотя и хорошие, упускают важный момент.
Методы в классах API (например, платформа .NET) образуют часть определения интерфейса (не интерфейс C # или VB, а интерфейс в смысле информатики).
Таким образом, обычно некорректно спрашивать, является ли вызов такого метода более быстрым, если только скорость не является частью формального определения интерфейса (в данном случае это не так).
Традиционно этот вид ярлыков (совмещающий поиск и извлечение) более эффективен независимо от языка, инфраструктуры, ОС, платформы или архитектуры компьютера. Это также более читабельно, потому что оно выражает ваше намерение явно, а не подразумевает его (из структуры вашего кода).
Таким образом, ответ (от старого хака) определенно «Да» (TryGetValue предпочтительнее комбинации ContainsKey и Item [Get] для получения значения из словаря).
Если вы думаете, что это звучит странно, подумайте об этом так: даже если текущие реализации TryGetValue, ContainsKey и Item [Get] не дают никакой разницы в скорости, вы можете предположить, что вполне вероятно, что будущая реализация (например, .NET v5) будет делать (TryGetValue будет быстрее). Подумайте о времени жизни вашего программного обеспечения.
Кроме того, интересно отметить, что типичные современные технологии определения интерфейса все еще редко предоставляют какие-либо средства для формального определения временных ограничений. Может быть .NET v5?
источник
Создание быстрой тестовой программы определенно улучшило использование TryGetValue с 1 миллионом элементов в словаре.
Полученные результаты:
ContainsKey + Item для 1000000 просмотров: 45ms
TryGetValue для 1000000 просмотров: 26мс
Вот тестовое приложение:
источник
На моей машине с нагрузками ОЗУ, при запуске в режиме RELEASE (не DEBUG),
ContainsKey
равноTryGetValue
/,try-catch
если все записи вDictionary<>
найдены.ContainsKey
значительно превосходит их все, когда есть только несколько словарных статей, которые не найдены (в моем примере ниже, установитеMAXVAL
что-то большее, чемENTRIES
пропуск некоторых записей):Полученные результаты:
Вот мой код:
источник
Помимо разработки микробенчмарка, который даст точные результаты в практической обстановке, вы можете проверить справочный источник .NET Framework.
System.Collections.Generic.Dictionary<TKey, TValue>.TryGetValue(TKey, out TValue)
System.Collections.Generic.Dictionary<TKey, TValue>.ContainsKey(TKey)
System.Collections.Generic.Dictionary<TKey, TValue>.Item(TKey)
Все они вызывают
FindEntry(TKey)
метод, который выполняет большую часть работы и не запоминает его результат, поэтому вызовTryGetValue
почти вдвое быстрее, чемContainsKey
+Item
.Неудобный интерфейс
TryGetValue
может быть адаптирован с помощью метода расширения :Начиная с C # 7.1, вы можете заменить
default(TValue)
на обычныйdefault
. Тип выведен.Использование:
Возвращается
null
для ссылочных типов, поиск которых не удался, если не указано явное значение по умолчанию.источник
Если вы пытаетесь извлечь значение из словаря, TryGetValue (ключ, выходное значение) является лучшим вариантом, но если вы проверяете наличие ключа, для новой вставки, без перезаписи старых ключей, и только с этой областью, ContainsKey (ключ) является лучшим вариантом, тест может подтвердить это:
Это истинный пример, у меня есть сервис, который для каждого созданного «Предмета» ассоциирует прогрессивный номер, этот номер, каждый раз, когда вы создаете новый элемент, должен быть найден свободным, если вы удаляете Элемент, свободный номер становится бесплатно, конечно, это не оптимизировано, так как у меня есть статическая переменная, которая кэширует текущее число, но если вы заканчиваете все числа, вы можете начать заново с 0 до UInt32.MaxValue
Тест выполнен:
Добавление элементов в хеш-
таблицу ... Выполнено в 0,5908 - пауза ....
Добавление элементов в словарь ...
Выполнено в 0,2679 - пауза ....
Поиск первого свободного числа для вставки
Первый метод : ContainsKey
Выполнено в 0,0561 - добавлено значение 1000000 в словаре - пауза ....
Второй метод: TryGetValue
Выполнено в 0,0643 - добавлено значение 1000001 в словаре - пауза ....
Проверка хеш-таблицы
Выполнено в 0, 3015 - добавлено значение 1000000 в хеш-таблицу - пауза ....
Нажмите любую клавишу для продолжения. ,
Если некоторые из вас могут спросить, может ли ContainsKeys иметь преимущество, я даже попытался инвертировать TryGetValue с ключом Contains, результат тот же.
Итак, для меня, в конечном итоге, все зависит от того, как ведет себя программа.
источник