.NET Framework
Словари
Поиск…
Перечисление словаря
Вы можете перечислить словарь через один из трех способов:
Использование пар KeyValue
Dictionary<int, string> dict = new Dictionary<int, string>();
foreach(KeyValuePair<int, string> kvp in dict)
{
Console.WriteLine("Key : " + kvp.Key.ToString() + ", Value : " + kvp.Value);
}
Использование клавиш
Dictionary<int, string> dict = new Dictionary<int, string>();
foreach(int key in dict.Keys)
{
Console.WriteLine("Key : " + key.ToString() + ", Value : " + dict[key]);
}
Использование значений
Dictionary<int, string> dict = new Dictionary<int, string>();
foreach(string s in dict.Values)
{
Console.WriteLine("Value : " + s);
}
Инициализация словаря с помощью инициализатора коллекции
// Translates to `dict.Add(1, "First")` etc.
var dict = new Dictionary<int, string>()
{
{ 1, "First" },
{ 2, "Second" },
{ 3, "Third" }
};
// Translates to `dict[1] = "First"` etc.
// Works in C# 6.0.
var dict = new Dictionary<int, string>()
{
[1] = "First",
[2] = "Second",
[3] = "Third"
};
Добавление в словарь
Dictionary<int, string> dict = new Dictionary<int, string>();
dict.Add(1, "First");
dict.Add(2, "Second");
// To safely add items (check to ensure item does not already exist - would throw)
if(!dict.ContainsKey(3))
{
dict.Add(3, "Third");
}
В качестве альтернативы они могут быть добавлены / установлены с помощью индексатора. (Индексатор внутренне выглядит как свойство, имеющее get и set, но принимает параметр любого типа, который указан между скобками):
Dictionary<int, string> dict = new Dictionary<int, string>();
dict[1] = "First";
dict[2] = "Second";
dict[3] = "Third";
В отличие от метода Add
который генерирует исключение, если ключ уже содержится в словаре, индексщик просто заменяет существующее значение.
Для использования в потоковом словаре слова ConcurrentDictionary<TKey, TValue>
:
var dict = new ConcurrentDictionary<int, string>();
dict.AddOrUpdate(1, "First", (oldKey, oldValue) => "First");
Получение значения из словаря
Учитывая этот код установки:
var dict = new Dictionary<int, string>()
{
{ 1, "First" },
{ 2, "Second" },
{ 3, "Third" }
};
Вы можете прочитать значение для записи с ключом 1. Если ключ не существует, значение будет KeyNotFoundException
, поэтому вы можете сначала проверить его с помощью ContainsKey
:
if (dict.ContainsKey(1))
Console.WriteLine(dict[1]);
У этого есть один недостаток: вы будете искать через свой словарь дважды (один раз, чтобы проверить наличие и один, чтобы прочитать значение). Для большого словаря это может повлиять на производительность. К счастью, обе операции могут выполняться вместе:
string value;
if (dict.TryGetValue(1, out value))
Console.WriteLine(value);
Сделать словарь с ключами Case-Insensivitve.
var MyDict = new Dictionary<string,T>(StringComparison.InvariantCultureIgnoreCase)
ConcurrentDictionary (из .NET 4.0)
Представляет потокобезопасную коллекцию пар ключ / значение, к которым одновременно можно обращаться несколькими потоками.
Создание экземпляра
Создание экземпляра работает почти так же, как с Dictionary<TKey, TValue>
, например:
var dict = new ConcurrentDictionary<int, string>();
Добавление или обновление
Вы можете быть удивлены, что нет метода Add
, но вместо этого есть AddOrUpdate
с 2 перегрузками:
(1) AddOrUpdate(TKey key, TValue, Func<TKey, TValue, TValue> addValue)
- добавляет пару ключ / значение, если ключ еще не существует, или обновляет пару ключ / значение с помощью указанной функции, если ключ уже существует.
(2) AddOrUpdate(TKey key, Func<TKey, TValue> addValue, Func<TKey, TValue, TValue> updateValueFactory)
- использует указанные функции, чтобы добавить пару ключ / значение к ключу, если ключ еще не существует, или обновить пару ключ / значение, если ключ уже существует.
Добавление или обновление значения, независимо от того, что было значением, если оно уже присутствовало для данного ключа (1):
string addedValue = dict.AddOrUpdate(1, "First", (updateKey, valueOld) => "First");
Добавление или обновление значения, но теперь изменение значения в обновлении на основе предыдущего значения (1):
string addedValue2 = dict.AddOrUpdate(1, "First", (updateKey, valueOld) => $"{valueOld} Updated");
Используя перегрузку (2), мы также можем добавить новое значение с использованием фабрики:
string addedValue3 = dict.AddOrUpdate(1, (key) => key == 1 ? "First" : "Not First", (updateKey, valueOld) => $"{valueOld} Updated");
Получение ценности
Получение значения аналогично значению Dictionary<TKey,TValue>
:
string value = null;
bool success = dict.TryGetValue(1, out value);
Получение или добавление значения
Есть две перегрузки mehod, которые будут получать или добавлять значение поточно-безопасным способом.
Получите значение с помощью ключа 2 или добавьте значение «Второе», если ключ отсутствует:
string theValue = dict.GetOrAdd(2, "Second");
Использование фабрики для добавления значения, если значение отсутствует:
string theValue2 = dict.GetOrAdd(2, (key) => key == 2 ? "Second" : "Not Second." );
IEnumerable to Dictionary (≥ .NET 3.5)
Создайте словарь <TKey, TValue> из IEnumerable <T> :
using System;
using System.Collections.Generic;
using System.Linq;
public class Fruits
{
public int Id { get; set; }
public string Name { get; set; }
}
var fruits = new[]
{
new Fruits { Id = 8 , Name = "Apple" },
new Fruits { Id = 3 , Name = "Banana" },
new Fruits { Id = 7 , Name = "Mango" },
};
// Dictionary<int, string> key value
var dictionary = fruits.ToDictionary(x => x.Id, x => x.Name);
Удаление из словаря
Учитывая этот код установки:
var dict = new Dictionary<int, string>()
{
{ 1, "First" },
{ 2, "Second" },
{ 3, "Third" }
};
Используйте метод Remove
для удаления ключа и связанного с ним значения.
bool wasRemoved = dict.Remove(2);
Выполнение этого кода удаляет ключ 2
и его значение из словаря. Remove
возвращает логическое значение, указывающее, был ли найден и удален указанный ключ из словаря. Если ключ не существует в словаре, ничего не удаляется из словаря, а возвращается false (исключение не генерируется).
Неправильно пытаться удалить ключ, установив для ключа значение null
.
dict[2] = null; // WRONG WAY TO REMOVE!
Это не приведет к удалению ключа. Он просто заменит предыдущее значение на значение null
.
Чтобы удалить все ключи и значения из словаря, используйте метод Clear
.
dict.Clear();
После выполнения Clear
в словаря Count
будет 0, а внутренняя емкость остается неизменной.
ContainsKey (TKey)
Чтобы проверить, имеет ли Dictionary
специальный ключ, вы можете вызвать метод ContainsKey(TKey)
и предоставить ключ типа TKey
. Метод возвращает значение bool
когда ключ существует в словаре. Для образца:
var dictionary = new Dictionary<string, Customer>()
{
{"F1", new Customer() { FirstName = "Felipe", ... } },
{"C2", new Customer() { FirstName = "Carl", ... } },
{"J7", new Customer() { FirstName = "John", ... } },
{"M5", new Customer() { FirstName = "Mary", ... } },
};
И проверьте, существует ли C2
в словаре:
if (dictionary.ContainsKey("C2"))
{
// exists
}
Метод ContainsKey доступен в стандартной версии Dictionary<TKey, TValue>
.
Словарь в список
Создание списка KeyValuePair:
Dictionary<int, int> dictionary = new Dictionary<int, int>();
List<KeyValuePair<int, int>> list = new List<KeyValuePair<int, int>>();
list.AddRange(dictionary);
Создание списка ключей:
Dictionary<int, int> dictionary = new Dictionary<int, int>();
List<int> list = new List<int>();
list.AddRange(dictionary.Keys);
Создание списка значений:
Dictionary<int, int> dictionary = new Dictionary<int, int>();
List<int> list = new List<int>();
list.AddRange(dictionary.Values);
ConcurrentDictionary, дополненный Lazy'1, уменьшает дублирование вычислений
проблема
ConcurrentDictionary сияет, когда дело доходит до моментального возврата существующих ключей из кеша, в основном блокировки и борьбы на гранулированном уровне. Но что, если создание объекта действительно дорого, перевешивает стоимость переключения контекста, и некоторые промахи промахов происходят?
Если один и тот же ключ запрашивается из нескольких потоков, один из объектов, возникающих в результате встречных операций, в конечном итоге будет добавлен в коллекцию, а остальные будут выброшены, теряя ресурсы ЦП для создания ресурса объекта и памяти для временного хранения объекта , Другие ресурсы также могут быть потрачены впустую. Это действительно плохо.
Решение
Мы можем комбинировать ConcurrentDictionary<TKey, TValue>
с Lazy<TValue>
. Идея заключается в том, что метод ConcurrentDictionary GetOrAdd может вернуть значение, фактически добавленное в коллекцию. В этом случае теряются и потери Lazy-объектов, но это не так уж сложно, так как сам Lazy-объект относительно невысок. Свойство Value проигрывающего Lazy никогда не запрашивается, потому что мы умны, чтобы запрашивать только свойство Value того, которое фактически добавлено в коллекцию, - тот, который возвращается из метода GetOrAdd:
public static class ConcurrentDictionaryExtensions
{
public static TValue GetOrCreateLazy<TKey, TValue>(
this ConcurrentDictionary<TKey, Lazy<TValue>> d,
TKey key,
Func<TKey, TValue> factory)
{
return
d.GetOrAdd(
key,
key1 =>
new Lazy<TValue>(() => factory(key1),
LazyThreadSafetyMode.ExecutionAndPublication)).Value;
}
}
Кэширование объектов XmlSerializer может быть особенно дорогостоящим, и в старте приложения также много споров. И есть еще кое-что: если это пользовательские сериализаторы, утечка памяти также будет продолжаться до конца жизненного цикла процесса. Единственное преимущество ConcurrentDictionary в этом случае заключается в том, что для остальной части жизненного цикла процесса не будет блокировок, но запуск приложения и использование памяти будут неприемлемыми. Это работа для нашего ConcurrentDictionary, дополненная Lazy:
private ConcurrentDictionary<Type, Lazy<XmlSerializer>> _serializers =
new ConcurrentDictionary<Type, Lazy<XmlSerializer>>();
public XmlSerializer GetSerialier(Type t)
{
return _serializers.GetOrCreateLazy(t, BuildSerializer);
}
private XmlSerializer BuildSerializer(Type t)
{
throw new NotImplementedException("and this is a homework");
}