Работа мечты в один клик 💼

💭Мечтаешь работать в Сбере, но не хочешь проходить десять кругов HR-собеседований? Теперь это проще, чем когда-либо!
💡AI-интервью за 15 минут – и ты уже на шаг ближе к своей новой работе.
Как получить оффер? 📌 Зарегистрируйся 📌 Пройди AI-интервью 📌 Получи обратную связь сразу же!
HR больше не тянут время – рекрутеры свяжутся с тобой в течение двух дней! 🚀
Реклама. ПАО СБЕРБАНК, ИНН 7707083893. Erid 2VtzquscAwp
Коллекция Dictionary<K, V>
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);
В примере выше Hello — ключ, Привет — значение, которые мы передаём в конструктор 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; }
}
Комментарии
Как было описано выше, скорость доступа к значениям словаря близка к O(1). Скорость зависит от качества алгоритма хеширования указанного типа TKey
. Значения ключей должны быть уникальными. Dictionary<TKey,TValue>
требует реализации равенства, чтобы определить, равны ли ключи. Можно указать реализацию универсального интерфейса с помощью конструктора, принимающего **IEqualityComparer<T>** **comparer**
параметр.
Например, можно использовать компараторы строк без учёта регистра, предоставляемые классом StringComparer
, для создания словарей с нечувствительными к регистру строковыми ключами.
Ёмкость a Dictionary<TKey,TValue>
— это количество элементов, которые Dictionary<TKey,TValue>
могут храниться. При добавлении элементов к объекту Dictionary<TKey,TValue>
ёмкость автоматически увеличивается, так как требуется перераспределить внутренний массив.
Итог
Словарь используется для быстрого доступа к значениям по ключу и содержит значения, которые можно получить по уникальным ключам. Имеет смысл использовать словарь, если есть большая коллекция данных, в которой часто производится поиск значений по какому-то ключу.
Комментарии