Коллекция Dictionary C#
Разбор и примеры работы методов коллекции Dictionary<K, V>: манипуляции со значениями, перебор словаря, конструкторы, реализации интерфейса и методы расширения. Dictionary<K, V> — класс пространства имён Рассмотрим способы создания и инициализации класса Например, пустой словарь: В примере выше тип ключа и значения указан Рассмотрим способ, когда при инициализации мы сразу же наполняем словарь: Каждое новое значение берётся в фигурные скобки: первое значение — ключ, второе значение — значение, которое будет доступно по ключу. Рассмотрим ещё один способ наполнения словаря: По сути, словарь — это коллекция, т. е. набор элементов, тип элементов — В примере выше Hello — ключ, Привет — значение, которые мы передаём в конструктор Также мы можем совместить два способа инициализации: Больше полезных материалов вы найдете на нашем телеграм-канале «Библиотека шарписта» Интересно, перейти к каналу Рассмотрим пример из предыдущего раздела и убедимся с помощью цикла Также можно перебрать словарь классическим циклом При попытке обратиться к элементу словаря с помощью Для получения значения по ключу необходимо использовать квадратные скобки: Рассмотрим пример работы с элементами словаря: Как можно заметить в примере выше, значение по ключу можно получить, заменить и даже создать новое. Добавление: Удаление: Получение: Прочее: Рассмотрим доступные конструкторы: Рассмотрим методы расширения, которые имеют отношение непосредственно к словарю. В реальности их гораздо больше, а, так как словарь реализовывает такие интерфейсы как Самый наглядный вариант реализации словаря — англо-русский словарь, но мы рассмотрим менее классические примеры: Как было описано выше, скорость доступа к значениям словаря близка к O(1). Скорость зависит от качества алгоритма хеширования указанного типа Например, можно использовать компараторы строк без учёта регистра, предоставляемые классом Ёмкость a Словарь используется для быстрого доступа к значениям по ключу и содержит значения, которые можно получить по уникальным ключам. Имеет смысл использовать словарь, если есть большая коллекция данных, в которой часто производится поиск значений по какому-то ключу. *** Коллекция Dictionary<K, V>
System.Collections.Generic
, который представляет из себя коллекцию ключей и значений, также называемый Словарем. Коллекция типизируется двумя типами: K
(key) — тип ключа и V
(value) — тип значения. Коллекция позволяет получать значения со скоростью близкой к O(1). Скорость зависит от качества алгоритма хеширования типа, заданного для ключа.Создания и инициализация словаря
Dictionary<K, V>
.
Dictionary<string, string> dict = new Dictionary<string, string>();
string
(строки).
Dictionary<string, string> dict = new Dictionary<string, string> { { "Hello", "Привет" }, { "How are you?", "Как дела?" }, { "Bye", "Пока" } };
Dictionary<string, string> dict = new Dictionary<string, string> { ["Hello"]= "Привет", ["How are you?"] = "Как дела?", ["Bye"] = "Пока", };
KeyValuePair
KeyValuePair<TKey, TValue>
, где TKey
— ключ, а TValue
— значение. Данная структура предоставляет свойства Key
и Value
, с помощью которых можно получить ключ и значение. Один из конструкторов Dictionary<TKey, TValue>
принимает на входе список элементов типа KeyValuePair<TKey, TValue>
. Рассмотрим пример:
var hello = new KeyValuePair<string, string>("Hello", "Привет"); var listForDict = new List<KeyValuePair<string, string>>() { hello }; Dictionary<string, string> dict = new Dictionary<string, string>(listForDict);
KeyValuePair<string, string>
, который, в свою очередь, передает в список **listForDict
**при инициализации в фигурных скобках и далее передает в конструктор словаря dict
.
var hello = new KeyValuePair<string, string>("Sorry", "Извиняюсь"); var listForDict = new List<KeyValuePair<string, string>>() { hello }; Dictionary<string, string> dict = new Dictionary<string, string>(listForDict) { ["Hello"]= "Привет", ["How are you?"] = "Как дела?", ["Bye"] = "Пока", };
Перебор словаря
foreach
, что в словаре действительно четыре элемента:
//Инициализируем словарь var hello = new KeyValuePair<string, string>("Sorry", "Извиняюсь"); var listForDict = new List<KeyValuePair<string, string>>() { hello }; Dictionary<string, string> dict = new Dictionary<string, string>(listForDict) { ["Hello"]= "Привет", ["How are you?"] = "Как дела?", ["Bye"] = "Пока", }; //Переберём значения словаря и выведем их foreach (var kvp in dict) Console.WriteLine($"Key:[{kvp.Key}] Value:[{kvp.Value}]");
for
:
for (int i = 0; i < dict.Count; i++) Console.WriteLine($"Key:[{dict.ElementAt(i).Key}] Value:[{dict.ElementAt(i).Value}]");
[]
, мы получим ошибку (для текущего словаря). Если ключи — тип int
, то можно получить исключение KeyNotFoundException
, если бы в словаре не было соответствующего ключа.Получение элементов
словарь[Ключ элемента]
Dictionary<string, string> dict = new Dictionary<string, string> { ["Hello"]= "Привет", ["How are you?"] = "Как дела?", ["Bye"] = "Пока", }; //Получим элемент по ключу Console.WriteLine(dict["Hello"]); //Изменим значение для ключа dict["Hello"] = "Здравствуйте"; Console.WriteLine(dict["Hello"]); //Добавим новое значение dict["I am fine"] = "Я в порядке"; Console.WriteLine(dict["I am fine"]);
Методы и свойства 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
.Примеры
// Допустим используем словарь для хранения номеров, но помним, // что ключ должен быть уникальным, иначе мы получим ошибку уникальных // значений. Dictionary<string, long> phones = new Dictionary<string, long> { { "Вася", 81112223344 }, { "Петя", 82224445566 }, { "Стёпа", 83335556677 } }; // Допустим у нас есть метод отправки смс по номеру void SendSMS(long phone, string msg) { // todo } // Допустим мы хотим стёпе отправить смс, получаем его номер по имени SendSMS(phones["Стёпа"], "Привет, как дела?"); // Допустим мы получили ответное смс var sms = GetSMS(phones["Стёпа"], "Привет, всё ок, а у тебя?"); // Воспользуемся методом, который позволит определить от кого смс и что пишет Console.WriteLine($"{WhoSendSMS(sms.phone)} - {sms.msg}" ); Console.WriteLine(); sms = GetSMS(81111111111, "Здравствуйте вам одобрена..."); // Получим смс от кого-то ещё) Console.WriteLine($"{WhoSendSMS(sms.phone)} - {sms.msg}"); Console.WriteLine(); // Допустим нам не понравился этот номер и мы не хотим больше от него получать смс // создадим ещё один словарь, с помощью него будем блокировать номера телефонов // и добавим туда наш новый номер с флагом false Dictionary<long, bool> accessNumbers = new Dictionary<long, bool> { { 81111111111, false } }; // Также добавим туда все номера из нашей записной книжки с флагом true foreach (var phone in phones) accessNumbers[phone.Value] = true; // Попробуем снова получить СМС var newSms = GetSMSWithAccess(81111111111, "Здравствуйте вам одобрена..."); // Поскольку теперь нам может прийти null нужно проверить есть ли действительно // смс if (newSms != null) Console.WriteLine($"{WhoSendSMS(newSms.phone)} - {newSms.msg}"); else Console.WriteLine($"Пришла СМС от заблокированного номера."); Console.WriteLine(); // Ну и проверим сообщение от Васи например newSms = GetSMSWithAccess(phones["Вася"], "Привет"); if (newSms != null) Console.WriteLine($"{WhoSendSMS(newSms.phone)} - {newSms.msg}"); Console.WriteLine(); // Допустим хотим посмотреть все номера в нашей записной книжке Console.WriteLine($"Всего номеров в записной книжке: {phones.Count}"); foreach (var phone in phones) Console.WriteLine($"{phone.Key} - {phone.Value}"); // Допустим мы видим, что у нас записан Петя, с которым давно // не общались и хотим удалить его номер Console.WriteLine(); phones.Remove("Петя"); Console.WriteLine($"Всего номеров в записной книжке: {phones.Count}"); // Проверим снова телефонную книгу foreach (var phone in phones) Console.WriteLine($"{phone.Key} - {phone.Value}"); Console.WriteLine(); // Как насчёт информации о доступных номерах? Console.WriteLine($"Всего записей в коллекции с доступными номерами: {accessNumbers.Count}"); foreach (var phone in accessNumbers) Console.WriteLine($"От номера {phone.Key} получать смс {(phone.Value ? "можно" : "нельзя")}."); // Допустим есть метод, который возвращает нам текст сообщения и номер телефона SMSInfo GetSMS(long phone, string msg) { return new SMSInfo { phone = phone, msg = msg }; } SMSInfo? GetSMSWithAccess(long phone, string msg) { if (accessNumbers[phone]) return new SMSInfo { phone = phone, msg = msg }; return null; } // А теперь попробуем найти обратно, кому же принадлежит номер string WhoSendSMS(long phone) { // Переберём все значения в словаре и найдём имя отправителя foreach (var phoneInfo in phones) { if (phoneInfo.Value == phone) return phoneInfo.Key; } return "Номер отсутствует в записной книжке!"; } // Для следующего примера создадим специальный класс class SMSInfo { public long phone { get; set; } public string msg { get; set; } }
Комментарии
TKey
. Значения ключей должны быть уникальными. Dictionary<TKey,TValue>
требует реализации равенства, чтобы определить, равны ли ключи. Можно указать реализацию универсального интерфейса с помощью конструктора, принимающего **IEqualityComparer<T>** **comparer**
параметр.StringComparer
, для создания словарей с нечувствительными к регистру строковыми ключами.Dictionary<TKey,TValue>
— это количество элементов, которые Dictionary<TKey,TValue>
могут храниться. При добавлении элементов к объекту Dictionary<TKey,TValue>
ёмкость автоматически увеличивается, так как требуется перераспределить внутренний массив.Итог
Материалы по теме
- 23 views
- 0 Comment