.NET Framework
Woordenboeken
Zoeken…
Een woordenboek opsommen
U kunt op een van de volgende 3 manieren een woordenboek opsommen:
KeyValue-paren gebruiken
Dictionary<int, string> dict = new Dictionary<int, string>();
foreach(KeyValuePair<int, string> kvp in dict)
{
Console.WriteLine("Key : " + kvp.Key.ToString() + ", Value : " + kvp.Value);
}
Sleutels gebruiken
Dictionary<int, string> dict = new Dictionary<int, string>();
foreach(int key in dict.Keys)
{
Console.WriteLine("Key : " + key.ToString() + ", Value : " + dict[key]);
}
Waarden gebruiken
Dictionary<int, string> dict = new Dictionary<int, string>();
foreach(string s in dict.Values)
{
Console.WriteLine("Value : " + s);
}
Een woordenboek initialiseren met een verzamelinitializer
// 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"
};
Toevoegen aan een woordenboek
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");
}
Als alternatief kunnen ze worden toegevoegd / ingesteld via de indexer. (Een indexer ziet er intern uit als een eigenschap, met een get en set, maar neemt een parameter van elk type dat tussen de haakjes wordt gespecificeerd):
Dictionary<int, string> dict = new Dictionary<int, string>();
dict[1] = "First";
dict[2] = "Second";
dict[3] = "Third";
In tegenstelling tot de methode Add
die een uitzondering genereert, vervangt de indexeerder alleen de bestaande waarde als er al een sleutel in het woordenboek staat.
Gebruik ConcurrentDictionary<TKey, TValue>
voor thread-safe woordenboek:
var dict = new ConcurrentDictionary<int, string>();
dict.AddOrUpdate(1, "First", (oldKey, oldValue) => "First");
Waarde verkrijgen uit een woordenboek
Gezien deze installatiecode:
var dict = new Dictionary<int, string>()
{
{ 1, "First" },
{ 2, "Second" },
{ 3, "Third" }
};
Misschien wilt u de waarde voor het item lezen met sleutel 1. Als de sleutel niet bestaat, krijgt KeyNotFoundException
een waarde als KeyNotFoundException
, dus wilt u dat eerst controleren met ContainsKey
:
if (dict.ContainsKey(1))
Console.WriteLine(dict[1]);
Dit heeft één nadeel: u zoekt twee keer in uw woordenboek (één keer om te controleren op bestaan en één om de waarde te lezen). Voor een groot woordenboek kan dit de prestaties beïnvloeden. Gelukkig kunnen beide bewerkingen samen worden uitgevoerd:
string value;
if (dict.TryGetValue(1, out value))
Console.WriteLine(value);
Maak een woordenboek met Case-Insensivitve-sleutels.
var MyDict = new Dictionary<string,T>(StringComparison.InvariantCultureIgnoreCase)
ConcurrentDictionary (vanaf .NET 4.0)
Vertegenwoordigt een thread-veilige verzameling sleutel / waarde-paren die tegelijkertijd toegankelijk zijn voor meerdere threads.
Een exemplaar maken
Een instantie maken werkt vrijwel op dezelfde manier als met Dictionary<TKey, TValue>
, bijvoorbeeld:
var dict = new ConcurrentDictionary<int, string>();
Toevoegen of bijwerken
Je zou verbaasd zijn, dat er geen Add
methode, maar in plaats daarvan is er AddOrUpdate
met 2 overbelasting:
(1) AddOrUpdate(TKey key, TValue, Func<TKey, TValue, TValue> addValue)
- Voegt een sleutel / waarde-paar toe als de sleutel nog niet bestaat, of werkt een sleutel / waarde-paar bij met behulp van de opgegeven functie als de sleutel bestaat al.
(2) AddOrUpdate(TKey key, Func<TKey, TValue> addValue, Func<TKey, TValue, TValue> updateValueFactory)
- Gebruikt de opgegeven functies om een sleutel / waardepaar toe te voegen aan de als de sleutel nog niet bestaat, of om update een sleutel / waarde-paar als de sleutel al bestaat.
Een waarde toevoegen of bijwerken, ongeacht de waarde als deze al aanwezig was voor de gegeven sleutel (1):
string addedValue = dict.AddOrUpdate(1, "First", (updateKey, valueOld) => "First");
Een waarde toevoegen of bijwerken, maar nu de waarde in update wijzigen, op basis van de vorige waarde (1):
string addedValue2 = dict.AddOrUpdate(1, "First", (updateKey, valueOld) => $"{valueOld} Updated");
Met behulp van de overbelasting (2) kunnen we ook nieuwe waarde toevoegen met behulp van een fabriek:
string addedValue3 = dict.AddOrUpdate(1, (key) => key == 1 ? "First" : "Not First", (updateKey, valueOld) => $"{valueOld} Updated");
Waarde krijgen
Een waarde verkrijgen is hetzelfde als bij het Dictionary<TKey,TValue>
:
string value = null;
bool success = dict.TryGetValue(1, out value);
Waarde krijgen of toevoegen
Er zijn twee mehod-overbelastingen die op een threadveilige manier een waarde krijgen of een waarde toevoegen .
Krijg waarde met sleutel 2, of voeg waarde "Tweede" toe als de sleutel niet aanwezig is:
string theValue = dict.GetOrAdd(2, "Second");
Een fabriek gebruiken om een waarde toe te voegen, als er geen waarde aanwezig is:
string theValue2 = dict.GetOrAdd(2, (key) => key == 2 ? "Second" : "Not Second." );
IEnumerable to Dictionary (≥ .NET 3.5)
Maak een woordenboek <TKey, TValue> van een 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);
Verwijderen uit een woordenboek
Gezien deze installatiecode:
var dict = new Dictionary<int, string>()
{
{ 1, "First" },
{ 2, "Second" },
{ 3, "Third" }
};
Gebruik de methode Remove
om een sleutel en de bijbehorende waarde te verwijderen.
bool wasRemoved = dict.Remove(2);
Als u deze code uitvoert, wordt sleutel 2
en de waarde uit het woordenboek verwijderd. Remove
retourneert een Booleaanse waarde die aangeeft of de opgegeven sleutel is gevonden en verwijderd uit het woordenboek. Als de sleutel niet bestaat in het woordenboek, wordt er niets uit het woordenboek verwijderd en wordt false geretourneerd (er wordt geen uitzondering gegenereerd).
Het is onjuist om te proberen een sleutel te verwijderen door de waarde voor de sleutel op null
.
dict[2] = null; // WRONG WAY TO REMOVE!
Hiermee wordt de sleutel niet verwijderd. Het vervangt gewoon de vorige waarde door een waarde van null
.
Gebruik de methode Clear
om alle sleutels en waarden uit een woordenboek te verwijderen.
dict.Clear();
Na het uitvoeren van Clear
de Count
het woordenboek 0, maar de interne capaciteit blijft ongewijzigd.
ContainsKey (TKey)
Om te controleren of een Dictionary
een specifieke sleutel heeft, kunt u de methode ContainsKey(TKey)
en de sleutel van het type TKey
. De methode retourneert een bool
waarde wanneer de sleutel in het woordenboek voorkomt. Voor monster:
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", ... } },
};
En controleer of er een C2
bestaat in het woordenboek:
if (dictionary.ContainsKey("C2"))
{
// exists
}
De methode ContainsKey is beschikbaar in de generieke versie Dictionary<TKey, TValue>
.
Woordenboek naar lijst
Een lijst met KeyValuePair maken:
Dictionary<int, int> dictionary = new Dictionary<int, int>();
List<KeyValuePair<int, int>> list = new List<KeyValuePair<int, int>>();
list.AddRange(dictionary);
Een lijst met sleutels maken:
Dictionary<int, int> dictionary = new Dictionary<int, int>();
List<int> list = new List<int>();
list.AddRange(dictionary.Keys);
Een zoeklijst maken:
Dictionary<int, int> dictionary = new Dictionary<int, int>();
List<int> list = new List<int>();
list.AddRange(dictionary.Values);
ConcurrentDictionary aangevuld met Lazy'1 vermindert dubbele berekening
Probleem
ConcurrentDictionary schittert als het gaat om het onmiddellijk retourneren van bestaande sleutels uit de cache, meestal vergrendeld en strijdend op granulair niveau. Maar wat als het maken van objecten echt duur is, opweegt tegen de kosten van contextomschakeling en er zich een aantal cache-missers voordoen?
Als dezelfde sleutel wordt aangevraagd bij meerdere threads, wordt een van de objecten die het gevolg zijn van botsende bewerkingen uiteindelijk toegevoegd aan de verzameling en worden de andere weggegooid, waardoor de CPU-bron wordt verspild om het object en geheugenbron te maken om het object tijdelijk op te slaan . Andere bronnen kunnen ook worden verspild. Dit is echt slecht.
Oplossing
We kunnen ConcurrentDictionary<TKey, TValue>
combineren met Lazy<TValue>
. Het idee is dat de methode ConcurrentDictionary GetOrAdd alleen de waarde kan retourneren die daadwerkelijk aan de collectie is toegevoegd. De losse Lazy-objecten kunnen ook in dit geval worden verspild, maar dat is niet echt een probleem, omdat het Lazy-object zelf relatief goedkoop is. De eigenschap Value van de verliezende Lazy wordt nooit aangevraagd, omdat we slim zijn om alleen de eigenschap Value aan te vragen van degene die daadwerkelijk aan de collectie is toegevoegd - degene die is geretourneerd via de GetOrAdd-methode:
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;
}
}
Caching van XmlSerializer-objecten kan bijzonder duur zijn en er is ook veel discussie bij het opstarten van de toepassing. En er is meer: als dit aangepaste serializers zijn, is er ook een geheugenlek voor de rest van de levenscyclus van het proces. Het enige voordeel van de ConcurrentDictionary is in dit geval dat er voor de rest van de levenscyclus van het proces geen vergrendelingen zijn, maar het opstarten van de toepassing en het geheugengebruik zijn onaanvaardbaar. Dit is een klus voor onze ConcurrentDictionary, aangevuld met 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");
}