Поиск…


Перечисление словаря

Вы можете перечислить словарь через один из трех способов:

Использование пар 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");
}


Modified text is an extract of the original Stack Overflow Documentation
Лицензировано согласно CC BY-SA 3.0
Не связан с Stack Overflow