前書き
Dictionary<TKey, TValue>
は,Key
からValue
への単方向辞書である.例えば図鑑番号をKey
,種族名をValue
としたポ〇モン図鑑Dictionary<int, string>
では,Key = 1
に対して"不思議種"というデータを$O(1)$操作で取得可能あるが,逆に種族名から図鑑番号を探索したいときは,$O(n)$操作の線形探索を行うしかない.今回は,Key
からValue
へのマッピングと同時に,その逆のValue
からKey
へのマッピングも保証する双方向辞書LinkedDictionary<TKey, TValue>
の作成を考える.
使用例
- 以下は,双方向辞書
LinkedDictionary<int, string>
の使用例である. Key<int>
からValue<string>
へのアクセスと,Value<string>
からKey<int>
へのアクセスを$O(1)$操作で実行可能である.
varpokomonLibrary=newLinkedDictionary<int,string>();pokomonLibrary.Add(0,"ィ゛ゃゾ┛A");pokomonLibrary.Add(6,"アネ゙デパミ");varpokomon1=pokomonLibrary[0];// "ィ゛ゃゾ┛A"varpokomon2=pokomonLibrary["ィ゛ゃゾ┛A"];// 0varpokomon3=pokomonLibrary[6];// "アネ゙デパミ"varpokomon4=pokomonLibrary["アネ゙デパミ"];// 6
実装
クラスのの全容は GitHub へ公開した.
https://github.com/Takuto168/Takuto168park/blob/master/LinkedDictionary.cs
以下に,その機能一覧と実装方法を示す.
クラスの作成
- ジェネリッククラス
LinkedDictionary<TKey, TValue>
を作成し,IDictionary<TKey, TValue>
インターフェースを継承する. - キーから値へのマッピングを保証するための
Dictionary<TKey, TValue>
フィールドと,値からキーへのマッピングを保証するためのDictionary<TValue, TKey>
フィールドを用意する.以降,これらを内部辞書と呼ぶ.
LinkedDictionary.cs
/// <summary>/// 双方向辞書/// キーから値へのマッピングと,その逆の値からキーへのマッピングを同時に保証するキーと値のコレクションを表します./// </summary>/// <typeparam name="TKey">ディクショナリ内のキーの型.</typeparam>/// <typeparam name="TValue">ディクショナリ内の値の型.</typeparam>publicclassLinkedDictionary<TKey,TValue>:IDictionary<TKey,TValue>{/// <summary>/// キーから値へのマッピング./// </summary>privateDictionary<TKey,TValue>_keyToValues;/// <summary>/// 値からキーへのマッピング./// </summary>privateDictionary<TValue,TKey>_valueToKeys;}
コンストラクタ
Dictionary<TKey, TValue>
のコンストラクタに倣い,下記の条件を指定できる幾つかのパターンのコンストラクタを定義する.- 空の
LinkedDictionary<TKey, TValue>
を作成 - 容量
Capacity
を明示的に指定して作成 - キーの型の既定の等値比較子
IEqualityComparer<TKey>
を指定して作成 - 値の型の既定の等値比較子
IEqualityComparer<TValue>
を指定して作成 IDictionary<TKey,TValue>
から要素をコピーして作成IEnumerable<KeyValuePair<TKey, TValue>>
から要素をコピーして作成
- 空の
#region->constructer
/// <summary>/// 空で,既定の初期量を備え,キーの型と値型の既定の等値比較子を使用する,LinkedDictionary<TKey,TValue> クラスの新しいインスタンスを初期化します./// </summary>publicLinkedDictionary():this(0,null,null){}/// <summary>/// 空で,指定した初期量を備え,キーの型と値型の既定の等値比較子を使用する,LinkedDictionary<TKey,TValue> クラスの新しいインスタンスを初期化します./// </summary>/// <param name="capacity">LinkedDictionary<TKey,TValue> が格納できる要素数の初期値.</param>publicLinkedDictionary(intcapacity):this(capacity,null,null){}/// <summary>/// 空で,既定の初期量を備え,指定した IEqualityComparer<TKey> を使用する,LinkedDictionary<TKey,TValue> クラスの新しいインスタンスを初期化します./// </summary>/// <param name="comparerKey">キーの比較時に使用する IEqualityComparer<TKey> 実装.キーの型の既定の EqualityComparer<TKey> を使用する場合は null.</param>publicLinkedDictionary(IEqualityComparer<TKey>comparerKey):this(0,comparerKey,null){}/// <summary>/// 空で,既定の初期量を備え,指定した IEqualityComparer<TValue> を使用する,LinkedDictionary<TKey,TValue> クラスの新しいインスタンスを初期化します./// </summary>/// <param name="comparerValue">値の比較時に使用する IEqualityComparer<TValue> 実装.値の型の既定の EqualityComparer<TValue> を使用する場合は null.</param>publicLinkedDictionary(IEqualityComparer<TValue>comparerValue):this(0,null,comparerValue){}/// <summary>/// 空で,既定の初期量を備え,指定した IEqualityComparer<TKey> と IEqualityComparer<TValue> を使用する,LinkedDictionary<TKey,TValue> クラスの新しいインスタンスを初期化します./// </summary>/// <param name="comparerKey">キーの比較時に使用する IEqualityComparer<TKey> 実装.キーの型の既定の EqualityComparer<TKey> を使用する場合は null.</param>/// <param name="comparerValue">値の比較時に使用する IEqualityComparer<TValue> 実装.値の型の既定の EqualityComparer<TValue> を使用する場合は null.</param>publicLinkedDictionary(IEqualityComparer<TKey>comparerKey,IEqualityComparer<TValue>comparerValue):this(0,comparerKey,comparerValue){}/// <summary>/// 空で,指定したの初期量を備え,指定した IEqualityComparer<TKey> と IEqualityComparer<TValue> を使用する,LinkedDictionary<TKey,TValue> クラスの新しいインスタンスを初期化します./// </summary>/// <param name="capacity">LinkedDictionary<TKey,TValue> が格納できる要素数の初期値.</param>/// <param name="comparerKey">キーの比較時に使用する IEqualityComparer<TKey> 実装.キーの型の既定の EqualityComparer<TKey> を使用する場合は null.</param>/// <param name="comparerValue">値の比較時に使用する IEqualityComparer<TValue> 実装.値の型の既定の EqualityComparer<TValue> を使用する場合は null.</param>publicLinkedDictionary(intcapacity,IEqualityComparer<TKey>comparerKey,IEqualityComparer<TValue>comparerValue){if(capacity<0)thrownewArgumentOutOfRangeException(nameof(capacity));this._keyToValues=newDictionary<TKey,TValue>(capacity,comparerKey);this._valueToKeys=newDictionary<TValue,TKey>(capacity,comparerValue);}/// <summary>/// 指定した IDictionary<TKey,TValue> から要素をコピーして格納し,キー型と値型の既定の等値比較子を使用する,LinkedDictionary<TKey,TValue> クラスの新しいインスタンスを初期化します./// </summary>/// <param name="dictionary">新しい LinkedDictionary<TKey,TValue> に要素をコピーする IDictionary<TKey,TValue>.</param>publicLinkedDictionary(IDictionary<TKey,TValue>dictionary):this(dictionary,null,null){}/// <summary>/// 指定した IDictionary<TKey,TValue> から要素をコピーして格納し,指定した IEqualityComparer<TKey> を使用する,LinkedDictionary<TKey,TValue> クラスの新しいインスタンスを初期化します./// </summary>/// <param name="dictionary">新しい LinkedDictionary<TKey,TValue> に要素をコピーする IDictionary<TKey,TValue>.</param>/// <param name="comparerKey">キーの比較時に使用する IEqualityComparer<TKey> 実装.キーの型の既定の EqualityComparer<TKey> を使用する場合は null.</param>publicLinkedDictionary(IDictionary<TKey,TValue>dictionary,IEqualityComparer<TKey>comparerKey):this(dictionary,comparerKey,null){}/// <summary>/// 指定した IDictionary<TKey,TValue> から要素をコピーして格納し,指定した IEqualityComparer<TValue> を使用する,LinkedDictionary<TKey,TValue> クラスの新しいインスタンスを初期化します./// </summary>/// <param name="dictionary">新しい LinkedDictionary<TKey,TValue> に要素をコピーする IDictionary<TKey,TValue>.</param>/// <param name="comparerValue">値の比較時に使用する IEqualityComparer<TValue> 実装.値の型の既定の EqualityComparer<TValue> を使用する場合は null.</param>publicLinkedDictionary(IDictionary<TKey,TValue>dictionary,IEqualityComparer<TValue>comparerValue):this(dictionary,null,comparerValue){}/// <summary>/// 指定した IDictionary<TKey,TValue> から要素をコピーして格納し,指定した IEqualityComparer<TKey> と IEqualityComparer<TValue> を使用する,LinkedDictionary<TKey,TValue> クラスの新しいインスタンスを初期化します./// </summary>/// <param name="dictionary">新しい LinkedDictionary<TKey,TValue> に要素をコピーする IDictionary<TKey,TValue>.</param>/// <param name="comparerKey">キーの比較時に使用する IEqualityComparer<TKey> 実装.キーの型の既定の EqualityComparer<TKey> を使用する場合は null.</param>/// <param name="comparerValue">値の比較時に使用する IEqualityComparer<TValue> 実装.値の型の既定の EqualityComparer<TValue> を使用する場合は null.</param>publicLinkedDictionary(IDictionary<TKey,TValue>dictionary,IEqualityComparer<TKey>comparerKey,IEqualityComparer<TValue>comparerValue):this(dictionary!=null?dictionary.Count:0,comparerKey,comparerValue){if(dictionary==null)thrownewArgumentNullException(nameof(dictionary));this.AddRange(dictionary);}/// <summary>/// 指定した IEnumerable<T> からコピーされた要素を格納する LinkedDictionary<TKey,TValue> クラスの新しいインスタンスを初期化します./// </summary>/// <param name="collection">新しい LinkedDictionary<TKey,TValue> に要素をコピーする Enumerable<KeyValuePair<TKey,TValue>>.</param>publicLinkedDictionary(IEnumerable<KeyValuePair<TKey,TValue>>collection):this(collection,null,null){}/// <summary>/// 指定した IEnumerable<KeyValuePair<TKey,TValue>> から要素をコピーして格納し,指定した IEqualityComparer<TKey> を使用する,LinkedDictionary<TKey, TValue>クラスの新しいインスタンスを初期化します./// </summary>/// <param name="collection">新しい LinkedDictionary<TKey,TValue> に要素をコピーする Enumerable<KeyValuePair<TKey,TValue>>.</param>/// <param name="comparerKey">キーの比較時に使用する IEqualityComparer<TKey> 実装.キーの型の既定の EqualityComparer<TKey> を使用する場合は null.</param>publicLinkedDictionary(IEnumerable<KeyValuePair<TKey,TValue>>collection,IEqualityComparer<TKey>comparerKey):this(collection,comparerKey,null){}/// <summary>/// 指定した IEnumerable<KeyValuePair<TKey,TValue>> から要素をコピーして格納し,指定した IEqualityComparer<TValue> を使用する,LinkedDictionary<TKey, TValue>クラスの新しいインスタンスを初期化します./// </summary>/// <param name="collection">新しい LinkedDictionary<TKey,TValue> に要素をコピーする Enumerable<KeyValuePair<TKey,TValue>>.</param>/// <param name="comparerValue">値の比較時に使用する IEqualityComparer<TValue> 実装.値の型の既定の EqualityComparer<TValue> を使用する場合は null.</param>publicLinkedDictionary(IEnumerable<KeyValuePair<TKey,TValue>>collection,IEqualityComparer<TValue>comparerValue):this(collection,null,comparerValue){}/// <summary>/// 指定した IEnumerable<KeyValuePair<TKey,TValue>> から要素をコピーして格納し,指定した IEqualityComparer<TKey> と IEqualityComparer<TValue> を使用する,LinkedDictionary<TKey, TValue>クラスの新しいインスタンスを初期化します./// </summary>/// <param name="collection">新しい LinkedDictionary<TKey,TValue> に要素をコピーする Enumerable<KeyValuePair<TKey,TValue>>.</param>/// <param name="comparerKey">キーの比較時に使用する IEqualityComparer<TKey> 実装.キーの型の既定の EqualityComparer<TKey> を使用する場合は null.</param>/// <param name="comparerValue">値の比較時に使用する IEqualityComparer<TValue> 実装.値の型の既定の EqualityComparer<TValue> を使用する場合は null.</param>publicLinkedDictionary(IEnumerable<KeyValuePair<TKey,TValue>>collection,IEqualityComparer<TKey>comparerKey,IEqualityComparer<TValue>comparerValue):this(collection!=null?collection.Count():0,comparerKey,comparerValue){if(collection==null)thrownewArgumentNullException(nameof(collection));this.AddRange(collection);}
プロパティ
- 明示的なインターフェースの実装では,基本的に内部辞書のプロパティを参照する.
- 通常の
TValue Item[TKey key]
プロパティに加え,逆探知を行うためのTKey Item[TValue value]
プロパティを用意する.ここで,set
のときは,一方の内部辞書のValue
を書き換えるとともに,他方のKey
を書き換える必要があることに留意する.
#region->property
/// <summary>/// ディクショナリのキーが等しいかどうかを確認するために使用する IEqualityComparer<TKey> を取得します./// </summary>publicIEqualityComparer<TKey>ComparerKey=>this._keyToValues.Comparer;/// <summary>/// ディクショナリの値が等しいかどうかを確認するために使用する IEqualityComparer<TValue> を取得します./// </summary>publicIEqualityComparer<TValue>ComparerValue=>this._valueToKeys.Comparer;/// <summary>/// LinkedDictionary<TKey,TValue> に格納されているキー/値ペアの数を取得します./// </summary>publicintCount=>this._keyToValues.Count;/// <summary>/// 指定されたキーに関連付けられた値を取得または設定します./// </summary>/// <param name="key">取得または設定する値のキー.</param>publicTValuethis[TKeykey]{get=>this.GetValue(key);set=>this.SetValue(key,value);}/// <summary>/// 指定された値に関連付けられたキーを取得または設定します./// </summary>/// <param name="argValue">取得または設定するキーの値.</param>publicTKeythis[TValueargValue]{get=>this.GetKey(argValue);set=>this.SetKey(argValue,value);}/// <summary>/// LinkedDictionary<TKey,TValue> 内のキーを格納しているコレクションを取得します./// </summary>publicICollection<TKey>Keys=>this._keyToValues.Keys;/// <summary>/// LinkedDictionary<TKey,TValue> 内の値を格納しているコレクションを取得します./// </summary>publicICollection<TValue>Values=>this._keyToValues.Values;/// <summary>/// IDictionary が読み取り専用かどうかを示す値を取得します./// </summary>publicboolIsReadOnly=>false;/// <summary>/// LinkedDictionary<TKey,TValue> 内のキー/値ペアのコレクションを取得します./// </summary>publicIEnumerable<KeyValuePair<TKey,TValue>>KeyValuePairs{get{foreach(variteminthis._keyToValues)yieldreturnitem;}}
メソッド
- 明示的なインターフェースの実装を含めた
Dictionary<TKey,TValue>
の実装機能に加え,いくつかの独自メソッドを作成した. - 独自メソッドの考案に際しては,Qiitaの記事よりDictionaryの拡張メソッド 36選を参考にさせて頂いた.
キー,値の取得
- キーと値の取得に関するいくつかのメソッドを定義する.
#region->method->get
/// <summary>/// 指定されたキーに関連付けられた値を取得します./// </summary>/// <param name="key">取得または設定する値のキー.</param>publicTValueGetValue(TKeykey){if(this.TryGetValue(key,outvarvalue))returnvalue;thrownewKeyNotFoundException();}/// <summary>/// 指定された値に関連付けられたキーを取得します./// </summary>/// <param name="value">取得または設定するキーの値.</param>publicTKeyGetKey(TValuevalue){if(this.TryGetKey(value,outvarkey))returnkey;thrownewKeyNotFoundException();}/// <summary>/// 指定されたキーに関連付けられた値の取得を試みます./// </summary>/// <param name="key">取得する値のキー.</param>/// <param name="value">キーが見つかった場合は,指定したキーに関連付けられている値が格納されます.それ以外の場合は value パラメーターの型に対する既定の値.</param>/// <returns>指定されたキーを持つ要素が LinkedDictionary<TKey,TValue> に含まれている場合は true,含まれていない場合は false.</returns>publicboolTryGetValue(TKeykey,outTValuevalue)=>this._keyToValues.TryGetValue(key,outvalue);/// <summary>/// 指定された値に関連付けられたキーの取得をを試みます./// </summary>/// <param name="value">取得するキーの値.</param>/// <param name="key">値が見つかった場合は,指定した値に関連付けられているキーが格納されます.それ以外の場合は key パラメーターの型に対する既定の値.</param>/// <returns>指定された値を持つ要素が LinkedDictionary<TKey,TValue> に含まれている場合は true,含まれていない場合は false.</returns>publicboolTryGetKey(TValuevalue,outTKeykey)=>this._valueToKeys.TryGetValue(value,outkey);/// <summary>/// 指定されたキーに関連付けられた値を取得します./// </summary>/// <param name="key">取得する値のキー.</param>/// <returns>値が見つかった場合は,指定した値に関連付けられているキー.それ以外の場合は value パラメーターの型に対する既定の値.</returns>publicTValueGetValueOrDefault(TKeykey)=>this.TryGetValue(key,outvarvalue)?value:default(TValue);/// <summary>/// 指定された値に関連付けられたキーを取得します./// </summary>/// <param name="value">取得するキーの値.</param>/// <returns>キーが見つかった場合は,指定した値に関連付けられている値.それ以外の場合は key パラメーターの型に対する既定の値.</returns>publicTKeyGetKeyOrDefault(TValuevalue)=>this.TryGetKey(value,outvarkey)?key:default(TKey);/// <summary>/// 指定されたキーに関連付けられた値を取得し,値が見つからなかった場合は指定した value を追加します./// </summary>/// <param name="key">取得する値のキー.</param>/// <param name="value">値が見つからなかった場合に追加する値.</param>/// <returns>値が見つかった場合は指定されたキーに関連付けられた値.見つからなかった場合は追加した値.</returns>publicTValueGetValueOrAdd(TKeykey,TValuevalue){this.TryAdd(key,value);returnthis._keyToValues[key];}/// <summary>/// 指定された値に関連付けられたキーを取得し,キーが見つからなかった場合は指定した key を追加します./// </summary>/// <param name="value">取得するキーの値.</param>/// <param name="key">キーが見つからなかった場合に追加するキー.</param>/// <returns>キーが見つかった場合は指定された値に関連付けられたキー.見つからなかった場合は追加したキー.</returns>publicTKeyGetKeyOrAdd(TValuevalue,TKeykey){this.TryAdd(key,value);returnthis._valueToKeys[value];}/// <summary>/// 指定されたキーに関連付けられた値を取得し,値が見つからなかった場合は指定した TValue 型に対する既定値を追加します./// </summary>/// <param name="key">取得する値のキー.</param>/// <returns>値が見つかった場合は指定されたキーに関連付けられた値.見つからなかった場合は TValue 型の既定値.</returns>publicTValueGetValueOrAddDefault(TKeykey)=>this.GetValueOrAdd(key,default(TValue));/// <summary>/// 指定された値に関連付けられたキーを取得し,キーが見つからなかった場合は指定した TKey 型に対する既定値を追加します./// </summary>/// <param name="value">取得するキーの値.</param>/// <returns>キーが見つかった場合は指定された値に関連付けられたキー.見つからなかった場合は TKey 型の既定値.</returns>publicTKeyGetKeyOrAddDefault(TValuevalue)=>this.GetKeyOrAdd(value,default(TKey));
キー,値の設定
- キーと値の設定に関するいくつかのメソッドを定義する.
- キーと値の設定では,一方の内部辞書の
Value
を書き換えるとともに,他方のKey
を書き換える必要があることに留意する.
#region->method->set
/// <summary>/// 指定されたキーに関連付けられた値を設定します./// </summary>/// <param name="key">設定する値のキー.</param>/// <param name="value">設定する値.</param>publicvoidSetValue(TKeykey,TValuevalue){if(!this.TrySetValue(key,value))thrownewKeyNotFoundException();}/// <summary>/// 指定された値に関連付けられたキーを設定します./// </summary>/// <param name="value">設定するキーの値.</param>/// <param name="key">設定するキー.</param>publicvoidSetKey(TValuevalue,TKeykey){if(!this.TrySetKey(value,key))thrownewKeyNotFoundException();}/// <summary>/// 指定されたキーに関連付けられた値の設定を試みます./// </summary>/// <param name="key">設定する値のキー.</param>/// <param name="value">設定する値.</param>/// <returns>指定されたキーを持つ要素が LinkedDictionary<TKey,TValue> に含まれている場合は true,含まれていない場合は false.</returns>publicboolTrySetValue(TKeykey,TValuevalue){if(this._keyToValues.ContainsKey(key)){varcurrentValue=this._keyToValues[key];this._keyToValues[key]=value;this._valueToKeys.Remove(currentValue);this._valueToKeys.Add(value,key);returntrue;}elsereturnfalse;}/// <summary>/// 指定された値に関連付けられたキーの設定を試みます./// </summary>/// <param name="value">設定するキーの値.</param>/// <param name="key">設定するキー.</param>/// <returns>指定された値を持つ要素が LinkedDictionary<TKey,TValue> に含まれている場合は true,含まれていない場合は false.</returns>publicboolTrySetKey(TValuevalue,TKeykey){if(this._valueToKeys.ContainsKey(value)){varcurrentKey=this._valueToKeys[value];this._valueToKeys[value]=key;this._keyToValues.Remove(currentKey);this._keyToValues.Add(key,value);returntrue;}elsereturnfalse;}/// <summary>/// 指定されたキーに関連付けられた値を設定または追加します./// </summary>/// <param name="key">設定する値のキー.</param>/// <param name="value">設定する値.</param>publicvoidSetValueOrAdd(TKeykey,TValuevalue){if(!this.TrySetValue(key,value))this.Add(key,value);}/// <summary>/// 指定された値に関連付けられたキーを設定または追加します./// </summary>/// <param name="key">設定するキーの値.</param>/// <param name="value">設定するキー.</param>publicvoidSetKeyOrAdd(TValuevalue,TKeykey){if(!this.TrySetKey(value,key))this.Add(key,value);}/// <summary>/// 指定されたキーに対して, TValue 型の既定値を設定または追加します./// </summary>/// <param name="key">設定する値のキー.</param>publicvoidSetValueOrAddDefault(TKeykey)=>this.SetValueOrAdd(key,default(TValue));/// <summary>/// 指定された値に対して, TKey 型の既定値を設定または追加します./// </summary>/// <param name="value">設定する値のキー.</param>publicvoidSeKeyOrAddDefault(TValuevalue)=>this.SetValueOrAdd(default(TKey),value);
要素の追加
- 要素の追加に関するいくつかのメソッドを定義する.
- 通常の
Dictionary<TKey,TValue>
では,追加するキーがnull
である場合に例外を発生させるが,LinkedDictionary<TKey,TValue>
では,キーと値のいずれかがnull
である場合に例外を発生させる. - 通常の
Dictionary<TKey,TValue>
では,追加するキーの重複によって例外を発生させるが,LinkedDictionary<TKey,TValue>
では,キーと値それぞれの重複によって例外を発生させる. - コレクションの追加を行う
AddRange
では,コレクション内の何れかのキーまたは値が重複した場合に例外を発生させる一方,TryAddRange
ではキーまたは値が重複しないもののみ追加を行う.
#region->method->add
/// <summary>/// 指定されたキーと値を LinkedDictionary<TKey,TValue> に追加します./// </summary>/// <param name="key">追加する要素のキー.</param>/// <param name="value">追加する要素の値.</param>publicvoidAdd(TKeykey,TValuevalue){if(!this.TryAdd(key,value))thrownewArgumentException();}/// <summary>/// 指定された KeyValuePair<TKey, TValue> を LinkedDictionary<TKey,TValue> に追加します./// </summary>/// <param name="item">追加する KeyValuePair<TKey, TValue>.</param>publicvoidAdd(KeyValuePair<TKey,TValue>item)=>this.Add(item.Key,item.Value);/// <summary>/// 指定された Tuple<TKey, TValue> を LinkedDictionary<TKey,TValue> に追加します./// </summary>/// <param name="item">追加する Tuple<TKey, TValue>.</param>publicvoidAdd((TKeyKey,TValueValue)item)=>this.Add(item.Key,item.Value);/// <summary>/// LinkedDictionary<TKey,TValue> に対して,指定されたキーと値の追加を試みます./// </summary>/// <param name="key">追加する要素のキー.</param>/// <param name="value">追加する要素の値.</param>/// <returns>キー/値ペアが LinkedDictionary<TKey,TValue> に追加された場合は true,それ以外の場合は false.</returns>publicboolTryAdd(TKeykey,TValuevalue){if(key==null)thrownewArgumentNullException(nameof(key));if(value==null)thrownewArgumentNullException(nameof(value));if(this._keyToValues.ContainsKey(key)||this._valueToKeys.ContainsKey(value))returnfalse;this._keyToValues.Add(key,value);this._valueToKeys.Add(value,key);returntrue;}/// <summary>/// LinkedDictionary<TKey,TValue> に対して,指定された KeyValuePair<TKey, TValue> の追加を試みます./// </summary>/// <param name="item">追加する KeyValuePair<TKey, TValue>.</param>/// <returns></returns>publicboolTryAdd(KeyValuePair<TKey,TValue>item)=>this.TryAdd(item.Key,item.Value);/// <summary>/// LinkedDictionary<TKey,TValue> に対して,指定された Tuple<TKey, TValue> の追加を試みます./// </summary>/// <param name="item">追加する Tuple<TKey, TValue>.</param>/// <returns></returns>publicboolTryAdd((TKeyKey,TValueValue)item)=>this.TryAdd(item.Key,item.Value);/// <summary>/// 指定した KeyValuePair<TKey, TValue> のコレクションを追加します./// </summary>/// <param name="collection">追加する KeyValuePair<TKey, TValue> のコレクション.</param>publicvoidAddRange(IEnumerable<KeyValuePair<TKey,TValue>>collection){foreach(varitemincollection)this.Add(item);}/// <summary>/// 指定した KeyValuePair<TKey, TValue> のコレクションのうち,キーと値が重複しないもののみを追加します./// </summary>/// <param name="collection">追加する KeyValuePair<TKey, TValue> のコレクション.</param>publicvoidTryAddRange(IEnumerable<KeyValuePair<TKey,TValue>>collection){foreach(varitemincollection)this.TryAdd(item);}
要素の削除
- 要素の削除に関するいくつかのメソッドを定義する.
- 要素の削除では,双方の内部辞書から削除を行うことに留意する.
#region->method->remove
/// <summary>/// LinkedDictionary<TKey,TValue> からすべてのキーと値を削除します./// </summary>publicvoidClear(){this._keyToValues.Clear();this._valueToKeys.Clear();}/// <summary>/// 指定したキーを持つ値を LinkedDictionary<TKey,TValue> から削除します./// </summary>/// <param name="key">削除する要素のキー.</param>/// <returns>要素が見つかり削除された場合は true.それ以外の場合は false.</returns>publicboolRemove(TKeykey)=>this.RemoveByKey(key);/// <summary>/// 指定したキーを持つ値を LinkedDictionary<TKey,TValue> から削除します./// </summary>/// <param name="key">削除する要素のキー.</param>/// <returns>要素が見つかり削除された場合は true.それ以外の場合は false.</returns>publicboolRemoveByKey(TKeykey)=>this.RemoveByKey(key,outvarvalue);/// <summary>/// 指定した値を持つキーを LinkedDictionary<TKey,TValue> から削除します./// </summary>/// <param name="value">削除する要素の値.</param>/// <returns>要素が見つかり削除された場合は true.それ以外の場合は false.</returns>publicboolRemove(TValuevalue)=>this.RemoveByValue(value);/// <summary>/// 指定した値を持つキーを LinkedDictionary<TKey,TValue> から削除します./// </summary>/// <param name="value">削除する要素の値.</param>/// <returns>要素が見つかり削除された場合は true.それ以外の場合は false.</returns>publicboolRemoveByValue(TValuevalue)=>this.RemoveByValue(value,outvarkey);/// <summary>/// 指定されたキーを持つ値を LinkedDictionary<TKey,TValue> から削除し,その要素の値を value パラメーターにコピーします./// </summary>/// <param name="key">削除する要素のキー.</param>/// <param name="value">削除された要素の値.</param>/// <returns>要素が見つかり削除された場合は true.それ以外の場合は false.</returns>publicboolRemove(TKeykey,outTValuevalue)=>this.RemoveByKey(key,outvalue);/// <summary>/// 指定されたキーを持つ値を LinkedDictionary<TKey,TValue> から削除し,その要素の値を value パラメーターにコピーします./// </summary>/// <param name="key">削除する要素のキー.</param>/// <param name="value">削除された要素の値.</param>publicboolRemoveByKey(TKeykey,outTValuevalue){if(this._keyToValues.ContainsKey(key)&&this._valueToKeys.ContainsKey(this._keyToValues[key])){value=this._keyToValues[key];this._keyToValues.Remove(key);this._valueToKeys.Remove(value);returntrue;}else{value=default(TValue);returnfalse;}}/// <summary>/// 指定された値を持つキーを LinkedDictionary<TKey,TValue> から削除し,その要素のキーを key パラメーターにコピーします./// </summary>/// <param name="value">削除する要素の値.</param>/// <param name="key">削除する要素のキー.</param>/// <returns>要素が見つかり削除された場合は true.それ以外の場合は false.</returns>publicboolRemove(TValuevalue,outTKeykey)=>this.RemoveByValue(value,outkey);/// <summary>/// 指定された値を持つキーを LinkedDictionary<TKey,TValue> から削除し,その要素のキーを key パラメーターにコピーします./// </summary>/// <param name="value">削除する要素の値.</param>/// <param name="key">削除する要素のキー.</param>/// <returns>要素が見つかり削除された場合は true.それ以外の場合は false.</returns>publicboolRemoveByValue(TValuevalue,outTKeykey){if(this._valueToKeys.ContainsKey(value)&&this._keyToValues.ContainsKey(this._valueToKeys[value])){key=this._valueToKeys[value];this._valueToKeys.Remove(value);this._keyToValues.Remove(key);returntrue;}else{key=default(TKey);returnfalse;}}/// <summary>/// 指定された KeyValuePair<TKey, TValue> を LinkedDictionary<TKey,TValue> から削除します./// </summary>/// <param name="item">削除する KeyValuePair<TKey, TValue>.</param>/// <returns>要素が見つかり削除された場合は true.それ以外の場合は false.</returns>publicboolRemove(KeyValuePair<TKey,TValue>item)=>this._keyToValues.ContainsKey(item.Key)&&this._keyToValues[item.Key].Equals(item.Value)&&this.Remove(item.Key);
判定
- 要素の判定に関するいくつかのメソッドを定義する.
- 実装は,内部辞書の処理に準じる.
#region->method->determinate
/// <summary>/// 指定したキーを持つ要素が LinkedDictionary<TKey,TValue> に含まれるかどうかを判断します./// </summary>/// <param name="key">検索するキー.</param>/// <returns>指定されたキーを持つ要素が LinkedDictionary<TKey,TValue> に含まれている場合は true,含まれていない場合は false.</returns>publicboolContainsKey(TKeykey)=>this._keyToValues.ContainsKey(key);/// <summary>/// 指定した値を持つ要素が LinkedDictionary<TKey,TValue> に含まれるかどうかを判断します./// </summary>/// <param name="value">検索する値.</param>/// <returns>指定された値を持つ要素が LinkedDictionary<TKey,TValue> に含まれている場合は true,含まれていない場合は false.</returns>publicboolContainsValue(TValuevalue)=>this._valueToKeys.ContainsKey(value);/// <summary>/// 指定した KeyValuePair<TKey, TValue> が LinkedDictionary<TKey,TValue> に含まれるかどうかを判断します./// </summary>/// <param name="item">検索する KeyValuePair<TKey, TValue></param>/// <returns></returns>publicboolContains(KeyValuePair<TKey,TValue>item)=>this.KeyValuePairs.Contains(item);
反復処理
- 反復処理を行うための
IEnumerator
メソッドを定義する. - 実装は,
Key
からValue
へのマッピングを示す`内部辞書の処理に準じる.
#region->method->IEnumerator
/// <summary>/// コレクションを反復処理する列挙子を返します./// </summary>/// <returns>コレクションの繰り返し処理に使用できる列挙子.</returns>publicIEnumerator<KeyValuePair<TKey,TValue>>GetEnumerator()=>this._keyToValues.GetEnumerator();/// <summary>/// コレクションを反復処理する列挙子を返します./// </summary>/// <returns>コレクションの反復処理に使用できる IEnumerator.</returns>IEnumeratorIEnumerable.GetEnumerator()=>this._keyToValues.GetEnumerator();
コピー,変換
- コピーと変換を行うためのいくつかのメソッドを定義する.
SelectValue
とSelectKey
は,双方向辞書内のValue
またはKey
に対して変換を行い新たな双方向辞書を生成する拡張メソッドからの応用.
#region->method->copy&convert
/// <summary>/// 指定した配列インデックスを開始位置として,配列に ICollection<T> の要素をコピーします./// </summary>/// <param name="array">ICollection<T> から要素がコピーされる 1 次元の配列. </param>/// <param name="index">array 内のコピーの開始位置を示すインデックス.</param>publicvoidCopyTo(KeyValuePair<TKey,TValue>[]array,intindex){if(array==null)thrownewArgumentNullException(nameof(array));if(index<0||index>array.Length)thrownewArgumentOutOfRangeException(nameof(index));if(array.Length-index<this.Count)thrownewArgumentException();foreach(variteminthis._keyToValues){array[index++]=newKeyValuePair<TKey,TValue>(item.Key,item.Value);}}/// <summary>/// シーケンスの各要素の値を Func<TValue, TValueResult> によって新しいフォームに射影し,既存のキーと新しい値による LinkedDictionary<TKey, TValueResult> を生成します./// </summary>/// <typeparam name="TValueResult">変換後の要素の値の型.</typeparam>/// <param name="valueSelector">要素の値の変換を表すデリゲート.</param>/// <returns>生成された LinkedDictionary<TKey, TValueResult>.</returns>publicLinkedDictionary<TKey,TValueResult>SelectValue<TValueResult>(Func<TValue,TValueResult>valueSelector)=>newLinkedDictionary<TKey,TValueResult>(this.KeyValuePairs.Select(item=>newKeyValuePair<TKey,TValueResult>(item.Key,valueSelector(item.Value))));/// <summary>/// シーケンスの各要素の値を Func<TKey, TKeyResult> によって新しいフォームに射影し,新しいキーと既存の値による LinkedDictionary<TKeyResult, TValue> を生成します./// </summary>/// <typeparam name="TKeyResult">変換後の要素のキーの型.</typeparam>/// <param name="keySelector">要素のキーの変換を表すデリゲート.</param>/// <returns>生成された LinkedDictionary<TKey, TValueResult>.</returns>publicLinkedDictionary<TKeyResult,TValue>SelectKey<TKeyResult>(Func<TKey,TKeyResult>keySelector)=>newLinkedDictionary<TKeyResult,TValue>(this.KeyValuePairs.Select(item=>newKeyValuePair<TKeyResult,TValue>(keySelector(item.Key),item.Value)));
ソート
TKey
とTValue
による既定のソートを行うメソッドを定義する.- 加えて,独自の変換デリゲートでもソートができるようにした.
#region->method->sort
///// <summary>///// 要素のキーの既定の比較子を使用して,LinkedDictionary<TKey,TValue> 全体内の要素をそのキーによって並べ替えます.///// </summary>publicvoidSortByKey()=>this.Sort(item=>item.Key);///// <summary>///// 要素の値の既定の比較子を使用して,LinkedDictionary<TKey,TValue> 全体内の要素をその値によって並べ替えます.///// </summary>publicvoidSortByValue()=>this.Sort(item=>item.Value);///// <summary>///// 指定した変換デリゲートを使用して,LinkedDictionary<TKey,TValue> 全体内の要素をその値によって並べ替えます.///// </summary>publicvoidSort<T>(Func<KeyValuePair<TKey,TValue>,T>Selecter){varcollection=this.KeyValuePairs.OrderBy(Selecter).ToArray();this.Clear();this.AddRange(collection);}
課題
TKey
とTValue
が同型であるとき,Item[TKey]
とItem[TValue]
プロパティや,Remove(TKey)
とRemove(TValue)
メソッド等で名前の競合が発生する.- 今回はそれぞれ別名のプロパティやメソッドを用意して回避可能としたが,他に良い方法は無いだろうか.例えば,
where TKey != TValue
のようなジェネリック型制約ができるとか…