👨🎓️ Учебник по C#: работа с коллекциями Dictionary<K, V>
Разбор и примеры работы методов коллекции Dictionary<K, V>: манипуляции со значениями, перебор словаря, конструкторы, реализации интерфейса и методы расширения.
Коллекция Dictionary<K, V>
Dictionary<K, V> — класс пространства имён System.Collections.Generic
, который представляет из себя коллекцию ключей и значений, также называемый Словарем. Коллекция типизируется двумя типами: K
(key) — тип ключа и V
(value) — тип значения. Коллекция позволяет получать значения со скоростью близкой к O(1). Скорость зависит от качества алгоритма хеширования типа, заданного для ключа.
Создание и инициализация словаря
Рассмотрим способы создания и инициализации класса Dictionary<K, V>
.
Например, пустой словарь:
В примере выше тип ключа и значения указан string
(строки).
Рассмотрим способ, когда при инициализации мы сразу же наполняем словарь:
Каждое новое значение берётся в фигурные скобки: первое значение — ключ, второе значение — значение, которое будет доступно по ключу.
Рассмотрим ещё один способ наполнения словаря:
KeyValuePair
По сути, словарь — это коллекция, т. е. набор элементов, тип элементов — KeyValuePair<TKey, TValue>
, где TKey
— ключ, а TValue
— значение. Данная структура предоставляет свойства Key
и Value
, с помощью которых можно получить ключ и значение. Один из конструкторов Dictionary<TKey, TValue>
принимает на входе список элементов типа KeyValuePair<TKey, TValue>
. Рассмотрим пример:
В примере выше Hello — ключ, Привет — значение, которые мы передаём в конструктор KeyValuePair<string, string>
, который, в свою очередь, передает в список **listForDict
**при инициализации в фигурных скобках и далее передает в конструктор словаря dict
.
Также мы можем совместить два способа инициализации:
Перебор словаря
Рассмотрим пример из предыдущего раздела и убедимся с помощью цикла foreach
, что в словаре действительно четыре элемента:
Также можно перебрать словарь классическим циклом for
:
При попытке обратиться к элементу словаря с помощью []
, мы получим ошибку (для текущего словаря). Если ключи — тип int
, то можно получить исключение KeyNotFoundException
, если бы в словаре не было соответствующего ключа.
Получение элементов
Для получения значения по ключу необходимо использовать квадратные скобки:
Рассмотрим пример работы с элементами словаря:
Как можно заметить в примере выше, значение по ключу можно получить, заменить и даже создать новое.
Методы и свойства Dictionary
Методы Dictionary
Добавление:
void Add(TKey key, TValue value)
— Добавляет элемент в коллекцию с ключомkey
и значениемvalue
bool TryAdd(TKey key, TValue value)
— Метод пытается добавить новый элемент в коллекцию. Если ключ в словаре не найден, то метод ничего не сделает и вернётfalse
.
Удаление:
bool Remove(TKey key)
— Удаляет элемент по ключу, при успехе возвращает —true
bool Remove(TKey key, out TValue value)
— Аналогично примеру выше, но ещё помещает значение удалённого элемента в выходной параметрvalue
.void Clear()
— Очищает словарь.
Получение:
bool TryGetValue(TKey key, out TValue value)
— Пытается получить значение по ключу, при успехе возвращаетtrue
и записывает полученное значение в переменнуюvalue
Прочее:
bool ContainsKey(TKey key)
— Проверяет наличие ключа в словаре.bool ContainsValue(TValue value)
— Проверяет наличие значения в словаре.int EnsureCapacity(int capacity)
— Обеспечивает возможность хранения указанного количества записей в словаре без дальнейшего увеличения его резервного хранилища. Возвращает текущее количество элементов в словаре.void TrimExcess()
— Устанавливает ёмкость словаря такой, какой бы она была, если словарь был изначально инициализирован со всеми записями.void TrimExcess(int capacity)
— Устанавливает ёмкость словаря такой, чтобы в нём помещалось указанное количество записей без дальнейшего увеличения его резервного хранилища. Еслиcapacity
меньше текущей ёмкости словаря, то генерирует исключениеArgumentOutOfRangeException
.
Свойства Dictionary
int Count { get; }
— Возвращает число элементов в словаре.IEqualityComparer<TKey> Comparer { get; }
— Возвращает интерфейсIEqualityComparer<T>
, используемый для установления равенства ключей словаря.Dictionary<TKey, TValue>.KeyCollection Keys { get; }
— Коллекция ключей.Dictionary<TKey, TValue>.ValueCollection Values { get; }
— Коллекция значений.TValue this[TKey key]
— Индексатор возвращает значение по заданному ключу.
Конструкторы
Рассмотрим доступные конструкторы:
public Dictionary(int capacity, IEqualityComparer<TKey>? comparer)
— Основной конструктор, задающий размер текущего словаря и задающий объект, реализующий интерфейсIEqualityComparer
, используемый для сравнения ключей. Входная переменнаяcapacity
, которую мы передаём, на самом деле не является реальным размером словаря. Начальный размер словаря выбирается из набора простых чисел (до 7199369) и выставляется равным или больше заданного числа, если же число элементов больше, чем максимальное число из набора, то дальше поиск размера идёт перебором с проверкой на простое значение до максимального значенияint(0x7fffffff — 2147483647)
. Инициализируются массивы выбранного ранее размера под наши ключи и значения и назначаетсяcomparer
.public Dictionary()
— Обычный конструктор без параметров, создаёт словарь, а при вызове вызывает другой конструктор с параметрамиthis(0, null)
.public Dictionary(int capacity)
— Аналогично конструкторам выше вызывает основной конструктор с параметрамиthis(capacity, null)
.public Dictionary(IEqualityComparer<TKey>? comparer)
— Аналогично конструктору выше вызывает основной конструктор с параметрамиthis(0, comparer)
.public Dictionary(IDictionary<TKey, TValue> dictionary)
— конструктор, принимающий один параметр: объект, реализующий интерфейсIDictionary
, вызывающий другой конструкторthis(dictionary, null)
, который, вызывает конструктор с передачей количества элементов иnull
дляcomparer
. Также сохраняет все элементы из переданного объекта с помощью методаAdd
.public Dictionary(IEnumerable<KeyValuePair<TKey, TValue>> collection, IEqualityComparer<TKey>? comparer)
— вызывает основной конструктор с передачей количества записей иcomparer
—this((collection as ICollection<KeyValuePair<TKey, TValue>>)?.Count ?? 0, comparer)
, проверяет коллекцию наnull
. Еслиnull
, то выбрасывает исключениеArgumentNullException
и заносит все значения с помощью методаAddRange(collection)
.public Dictionary(IDictionary<TKey, TValue> dictionary, IEqualityComparer<TKey>? comparer)
— вызывает основной конструктор с параметрамиthis(dictionary != null ? dictionary.Count : 0, comparer)
и проверяет словарь наnull
, еслиnull
, то выбрасывает исключениеArgumentNullException
и заносит все значения с помощью методаAddRange(dictionary)
.public Dictionary(IEnumerable<KeyValuePair<TKey, TValue>> collection)
— вызывает другой конструктор с параметрамиthis(collection, null)
.
Явные реализации интерфейса
ICollection.CopyTo(Array, Int32)
— Копирует элементы коллекцииICollection<T>
в массив, начиная с указанного индекса массива.ICollection.IsSynchronized
— Получает значение, определяющее, является ли доступ к коллекцииICollection
синхронизированным (потокобезопасным).ICollection.SyncRoot
— Получает объект, с помощью которого можно синхронизировать доступ к коллекцииICollection
.IDictionary.Add(Object, Object)
— Добавляет указанные ключ и значение в словарь.IDictionary.Contains(Object)
— Определяет, содержится ли элемент с указанным ключом вIDictionary
.IDictionary.Keys
— Возвращает интерфейсICollection
, содержащий ключиIDictionary
.IDictionary.Values
— Возвращает интерфейсICollection
, содержащий значения изIDictionary
.
Методы расширения
Рассмотрим методы расширения, которые имеют отношение непосредственно к словарю. В реальности их гораздо больше, а, так как словарь реализовывает такие интерфейсы как ICollection
, IEnumerable
, IDeserializationCallback
их ещё больше (больше информации).
Remove<TKey,TValue>(IDictionary<TKey,TValue>, TKey, TValue)
— Пытается удалить значение с указаннымkey
изdictionary
.GetValueOrDefault<TKey,TValue>(IReadOnlyDictionary<TKey,TValue>, TKey)
— Пытается получить значение, связанное с указаннымkey
вdictionary
.TryAdd<TKey,TValue>(IDictionary<TKey,TValue>, TKey, TValue)
— Пытается добавить указанные элементыkey
иvalue
вdictionary
.
Примеры
Самый наглядный вариант реализации словаря — англо-русский словарь, но мы рассмотрим менее классические примеры:
Комментарии
Как было описано выше, скорость доступа к значениям словаря близка к O(1). Скорость зависит от качества алгоритма хеширования указанного типа TKey
. Значения ключей должны быть уникальными. Dictionary<TKey,TValue>
требует реализации равенства, чтобы определить, равны ли ключи. Можно указать реализацию универсального интерфейса с помощью конструктора, принимающего **IEqualityComparer<T>** **comparer**
параметр.
Например, можно использовать компараторы строк без учёта регистра, предоставляемые классом StringComparer
, для создания словарей с нечувствительными к регистру строковыми ключами.
Ёмкость a Dictionary<TKey,TValue>
— это количество элементов, которые Dictionary<TKey,TValue>
могут храниться. При добавлении элементов к объекту Dictionary<TKey,TValue>
ёмкость автоматически увеличивается, так как требуется перераспределить внутренний массив.
Итог
Словарь используется для быстрого доступа к значениям по ключу и содержит значения, которые можно получить по уникальным ключам. Имеет смысл использовать словарь, если есть большая коллекция данных, в которой часто производится поиск значений по какому-то ключу.