.NET Framework
Słowniki
Szukaj…
Wyliczanie słownika
Możesz wyliczyć za pomocą słownika na jeden z 3 sposobów:
Korzystanie z par 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);
}
Używanie kluczy
Dictionary<int, string> dict = new Dictionary<int, string>();
foreach(int key in dict.Keys)
{
Console.WriteLine("Key : " + key.ToString() + ", Value : " + dict[key]);
}
Używanie wartości
Dictionary<int, string> dict = new Dictionary<int, string>();
foreach(string s in dict.Values)
{
Console.WriteLine("Value : " + s);
}
Inicjowanie słownika za pomocą inicjatora kolekcji
// 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"
};
Dodawanie do słownika
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");
}
Alternatywnie można je dodawać / ustawiać za pomocą indeksu. (Indeksator wewnętrznie wygląda jak właściwość, która ma get i set, ale przyjmuje parametr dowolnego typu określony w nawiasach):
Dictionary<int, string> dict = new Dictionary<int, string>();
dict[1] = "First";
dict[2] = "Second";
dict[3] = "Third";
W przeciwieństwie do metody Add
, która zgłasza wyjątek, jeśli klucz jest już zawarty w słowniku, indeksator po prostu zastępuje istniejącą wartość.
Aby uzyskać słownik bezpieczny dla wątków, użyj ConcurrentDictionary<TKey, TValue>
:
var dict = new ConcurrentDictionary<int, string>();
dict.AddOrUpdate(1, "First", (oldKey, oldValue) => "First");
Uzyskiwanie wartości ze słownika
Biorąc pod uwagę ten kod instalacyjny:
var dict = new Dictionary<int, string>()
{
{ 1, "First" },
{ 2, "Second" },
{ 3, "Third" }
};
Możesz przeczytać wartość wpisu za pomocą klucza 1. Jeśli klucz nie istnieje, uzyskanie wartości spowoduje KeyNotFoundException
, więc możesz najpierw sprawdzić to za pomocą ContainsKey
:
if (dict.ContainsKey(1))
Console.WriteLine(dict[1]);
Ma to jedną wadę: przeszukujesz słownik dwa razy (raz, aby sprawdzić istnienie, a drugi, aby odczytać wartość). W przypadku dużego słownika może to mieć wpływ na wydajność. Na szczęście obie operacje można wykonywać razem:
string value;
if (dict.TryGetValue(1, out value))
Console.WriteLine(value);
Stwórz słownik z kluczami rozróżniającymi wielkość liter.
var MyDict = new Dictionary<string,T>(StringComparison.InvariantCultureIgnoreCase)
ConcurrentDictionary (z .NET 4.0)
Reprezentuje bezpieczną dla wątku kolekcję par klucz / wartość, do której można uzyskać dostęp jednocześnie przez wiele wątków.
Tworzenie instancji
Tworzenie instancji działa prawie tak samo, jak w przypadku Dictionary<TKey, TValue>
, np .:
var dict = new ConcurrentDictionary<int, string>();
Dodawanie lub aktualizacja
Możesz być zaskoczony, że nie ma metody Add
, ale zamiast tego istnieje AddOrUpdate
z 2 przeciążeniami:
(1) AddOrUpdate(TKey key, TValue, Func<TKey, TValue, TValue> addValue)
- Dodaje parę klucz / wartość, jeśli klucz jeszcze nie istnieje, lub aktualizuje parę klucz / wartość, używając określonej funkcji, jeśli klucz już istnieje.
(2) AddOrUpdate(TKey key, Func<TKey, TValue> addValue, Func<TKey, TValue, TValue> updateValueFactory)
- Używa określonych funkcji w celu dodania pary klucz / wartość do, jeśli klucz jeszcze nie istnieje, lub do zaktualizuj parę klucz / wartość, jeśli klucz już istnieje.
Dodawanie lub aktualizowanie wartości, bez względu na wartość, jeśli była już obecna dla danego klucza (1):
string addedValue = dict.AddOrUpdate(1, "First", (updateKey, valueOld) => "First");
Dodawanie lub aktualizowanie wartości, ale teraz zmienianie wartości w aktualizacji na podstawie poprzedniej wartości (1):
string addedValue2 = dict.AddOrUpdate(1, "First", (updateKey, valueOld) => $"{valueOld} Updated");
Za pomocą przeciążenia (2) możemy również dodać nową wartość przy użyciu fabryki:
string addedValue3 = dict.AddOrUpdate(1, (key) => key == 1 ? "First" : "Not First", (updateKey, valueOld) => $"{valueOld} Updated");
Zdobywanie wartości
Uzyskanie wartości jest takie samo jak w przypadku Dictionary<TKey,TValue>
:
string value = null;
bool success = dict.TryGetValue(1, out value);
Uzyskiwanie lub dodawanie wartości
Istnieją dwa podstawowe przeciążenia, które uzyskają lub dodadzą wartość w sposób bezpieczny dla wątków.
Uzyskaj wartość za pomocą klucza 2 lub dodaj wartość „Drugi”, jeśli klucz nie jest obecny:
string theValue = dict.GetOrAdd(2, "Second");
Korzystanie z fabryki w celu dodania wartości, jeśli wartość nie jest obecna:
string theValue2 = dict.GetOrAdd(2, (key) => key == 2 ? "Second" : "Not Second." );
IEliczalny do słownika (≥ .NET 3.5)
Utwórz słownik <TKey, TValue> z 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);
Usuwanie ze słownika
Biorąc pod uwagę ten kod instalacyjny:
var dict = new Dictionary<int, string>()
{
{ 1, "First" },
{ 2, "Second" },
{ 3, "Third" }
};
Użyj metody Remove
, aby usunąć klucz i powiązaną z nim wartość.
bool wasRemoved = dict.Remove(2);
Wykonanie tego kodu usuwa klucz 2
i jego wartość ze słownika. Remove
zwraca wartość logiczną wskazującą, czy określony klucz został znaleziony i usunięty ze słownika. Jeśli klucz nie istnieje w słowniku, nic nie jest usuwane ze słownika i zwracana jest wartość false (nie jest zgłaszany wyjątek).
Niepoprawne jest próbowanie usunięcia klucza poprzez ustawienie wartości klucza na null
.
dict[2] = null; // WRONG WAY TO REMOVE!
To nie usunie klucza. Po prostu zastąpi poprzednią wartość wartością null
.
Aby usunąć wszystkie klucze i wartości ze słownika, użyj metody Clear
.
dict.Clear();
Po wykonaniu Clear
słownika za Count
będzie 0, ale pojemność wewnętrzna pozostaje niezmieniona.
ContainsKey (TKey)
Aby sprawdzić, czy Dictionary
ma określony klucz, możesz wywołać metodę ContainsKey(TKey)
i podać klucz typu TKey
. Metoda zwraca wartość bool
gdy klucz istnieje w słowniku. Na próbkę:
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", ... } },
};
I sprawdź, czy w słowniku istnieje C2
:
if (dictionary.ContainsKey("C2"))
{
// exists
}
Metoda ContainsKey jest dostępna w ogólnej wersji Dictionary<TKey, TValue>
.
Słownik do listy
Tworzenie listy KeyValuePair:
Dictionary<int, int> dictionary = new Dictionary<int, int>();
List<KeyValuePair<int, int>> list = new List<KeyValuePair<int, int>>();
list.AddRange(dictionary);
Tworzenie listy kluczy:
Dictionary<int, int> dictionary = new Dictionary<int, int>();
List<int> list = new List<int>();
list.AddRange(dictionary.Keys);
Tworzenie listy wartości:
Dictionary<int, int> dictionary = new Dictionary<int, int>();
List<int> list = new List<int>();
list.AddRange(dictionary.Values);
ConcurrentDictionary wzbogacony o Lazy'1 zmniejsza powielone obliczenia
Problem
ConcurrentDictionary świeci, jeśli chodzi o natychmiastowe zwracanie istniejących kluczy z pamięci podręcznej, głównie bez blokady i rywalizujących na poziomie granularnym. Ale co, jeśli tworzenie obiektów jest naprawdę drogie, przewyższając koszt przełączania kontekstu i występują pewne błędy w pamięci podręcznej?
Jeśli ten sam klucz zostanie zażądany z wielu wątków, jeden z obiektów powstających w wyniku kolizji zostanie ostatecznie dodany do kolekcji, a pozostałe zostaną wyrzucone, marnując zasoby procesora do utworzenia obiektu i zasobu pamięci do tymczasowego przechowywania obiektu . Inne zasoby również mogą zostać zmarnowane. To jest naprawdę złe.
Rozwiązanie
Możemy łączyć ConcurrentDictionary<TKey, TValue>
z Lazy<TValue>
. Chodzi o to, że metoda ConcurrentDictionary GetOrAdd może zwrócić tylko wartość, która faktycznie została dodana do kolekcji. Utracone obiekty Leniwe mogą zostać zmarnowane również w tym przypadku, ale nie stanowi to większego problemu, ponieważ sam Lazy obiekt jest stosunkowo niedrogi. Właściwość Value przegrywającego Leniwa nigdy nie jest wymagana, ponieważ jesteśmy mądrzy, aby zażądać tylko właściwości Value tej faktycznie dodanej do kolekcji - tej zwróconej z metody 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;
}
}
Buforowanie obiektów XmlSerializer może być szczególnie kosztowne, a przy uruchamianiu aplikacji jest również sporo sporów. Jest jeszcze coś więcej: jeśli są to niestandardowe serializatory, nastąpi przeciek pamięci również przez resztę cyklu życia procesu. Jedyną zaletą ConcurrentDictionary w tym przypadku jest to, że przez resztę cyklu życia procesu nie będzie żadnych blokad, ale uruchomienie aplikacji i użycie pamięci byłoby niedopuszczalne. To jest praca dla naszego ConcurrentDictionary, wzbogacona o 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");
}