サーチ…
辞書の列挙
ディクショナリを3つの方法のいずれかで列挙できます。
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
。そのため、まずContainsKey
使用してその値をチェックしContainsKey
。
if (dict.ContainsKey(1))
Console.WriteLine(dict[1]);
これには欠点が1つあります。辞書を2回検索します(存在を確認するために1回、値を読み取るために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
メソッドがないのAdd
驚くかもしれませんが、代わりに2つのオーバーロードを持つAddOrUpdate
がありAddOrUpdate
:
(1) AddOrUpdate(TKey key, TValue, Func<TKey, TValue, TValue> addValue)
- キーが存在しない場合はキー/値のペアを追加し、キーが存在する場合は指定された関数を使用してキー/もう存在している。
(2) AddOrUpdate(TKey key, Func<TKey, TValue> addValue, Func<TKey, TValue, TValue> updateValueFactory)
- 指定された関数を使用して、キーが存在しない場合、またはキーがすでに存在する場合は、キーと値のペアを更新します。
指定されたkey(1)にすでに存在していた場合、値が何であっても値を追加または更新する:
string addedValue = dict.AddOrUpdate(1, "First", (updateKey, valueOld) => "First");
値を追加または更新するが、以前の値(1)に基づいて更新の値を変更する:
string addedValue2 = dict.AddOrUpdate(1, "First", (updateKey, valueOld) => $"{valueOld} Updated");
overload(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);
値の取得または追加
スレッドセーフな方法で値を取得または追加する2つのメソッドオーバーロードがあります。
キー2で値を取得するか、キーが存在しない場合に値「秒」を追加します。
string theValue = dict.GetOrAdd(2, "Second");
値が存在しない場合、ファクトリを使用して値を追加する:
string theValue2 = dict.GetOrAdd(2, (key) => key == 2 ? "Second" : "Not Second." );
IEnumerableからDictionary(≧.NET 3.5)
IEnumerable <T>からDictionary <TKey、TValue>を作成します。
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
値に置き換えnull
。
辞書からすべてのキーと値を削除するには、 Clear
メソッドを使用し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は、キャッシュから既存のキーを即座に返すことになります。ほとんどの場合、ロックがなく、細かいレベルで競合します。しかし、オブジェクトの作成が本当に高価で、コンテキスト切り替えのコストを上回り、キャッシュミスが発生する場合はどうでしょうか?
複数のスレッドから同じキーが要求された場合、結果的に衝突するオブジェクトの1つがコレクションに追加され、残りのオブジェクトが破棄され、オブジェクトを作成するCPUリソースとオブジェクトを一時的に格納するメモリリソースが無駄になります。他のリソースも無駄になる可能性があります。これは本当に悪いです。
溶液
ConcurrentDictionary<TKey, TValue>
とLazy<TValue>
を組み合わせることができます。アイデアは、ConcurrentDictionaryのGetOrAddメソッドは、実際にコレクションに追加された値だけを返すことができるということです。 Lazyオブジェクトは、この場合も無駄になる可能性がありますが、Lazyオブジェクト自体が比較的無計画であるため、あまり問題にはなりません。実際にコレクションに追加されたValueプロパティ(GetOrAddメソッドから返されたもの)のみを要求するのは賢明なので、失うLazyのValueプロパティは要求されません。
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");
}