.NET基本クラスライブラリに、重複キーの使用を許可するディクショナリクラスはありますか?私が見つけた唯一の解決策は、例えば、次のようなクラスを作成することです:
Dictionary<string, List<object>>
しかし、これは実際に使用するのは非常にいらいらします。 Javaでは、MultiMapでこれを実現できますが、.NETでアナログを見つけることはできません。
.NET 3.5を使用している場合は、 Lookup
クラスを使用します。
編集:通常、 Enumerable.ToLookup
を使用してLookup
を作成します。これは、後で変更する必要がないことを前提としていますが、通常はこれで十分であることがわかります。
それがうまくいかない場合、フレームワークに役立つものはないと思います-そして辞書を使うことはそれと同じくらい良いです取得 :(
Listクラスは、コレクションを反復処理する重複を含むキー/値コレクションに対して実際に非常にうまく機能します。例:
List<KeyValuePair<string, string>> list = new List<KeyValuePair<string, string>>();
// add some values to the collection here
for (int i = 0; i < list.Count; i++)
{
Print(list[i].Key, list[i].Value);
}
List <KeyValuePair <string、string>>でこれを行う1つの方法を次に示します。
public class ListWithDuplicates : List<KeyValuePair<string, string>>
{
public void Add(string key, string value)
{
var element = new KeyValuePair<string, string>(key, value);
this.Add(element);
}
}
var list = new ListWithDuplicates();
list.Add("k1", "v1");
list.Add("k1", "v2");
list.Add("k1", "v3");
foreach(var item in list)
{
string x = string.format("{0}={1}, ", item.Key, item.Value);
}
出力k1 = v1、k1 = v2、k1 = v3
キーと値の両方として文字列を使用している場合、 System.Collections.Specialized.NameValueCollection を使用できます。これは、GetValues(string key)メソッドを介して文字列値の配列を返します。
とりわけ、MultiDictionaryと呼ばれるクラスを含む PowerCollections ライブラリに出会いました。これは、このタイプの機能をきれいにラップします。
Lookupの使用に関する非常に重要な注意:
Lookup(TKey, TElement)
を実装するオブジェクトでToLookup
を呼び出すことにより、IEnumerable(T)
のインスタンスを作成できます。
Lookup(TKey, TElement)
の新しいインスタンスを作成するパブリックコンストラクターはありません。また、Lookup(TKey, TElement)
オブジェクトは不変です。つまり、Lookup(TKey, TElement)
オブジェクトの作成後は、要素またはキーを追加または削除できません。
私はこれがほとんどの用途のショーストッパーになると思います。
List<KeyValuePair<object, object>>
のようなものがジョブを実行すると思います。
> = .NET 4を使用している場合、Tuple
クラスを使用できます。
// declaration
var list = new List<Tuple<string, List<object>>>();
// to add an item to the list
var item = Tuple<string, List<object>>("key", new List<object>);
list.Add(item);
// to iterate
foreach(var i in list)
{
Console.WriteLine(i.Item1.ToString());
}
「重複キー」エントリを許可する辞書の「独自の」バージョンを作成するのは簡単です。大まかな簡単な実装を次に示します。 IDictionary<T>
で基本的にほとんど(すべてではないにしても)のサポートを追加することを検討する必要があります。
public class MultiMap<TKey,TValue>
{
private readonly Dictionary<TKey,IList<TValue>> storage;
public MultiMap()
{
storage = new Dictionary<TKey,IList<TValue>>();
}
public void Add(TKey key, TValue value)
{
if (!storage.ContainsKey(key)) storage.Add(key, new List<TValue>());
storage[key].Add(value);
}
public IEnumerable<TKey> Keys
{
get { return storage.Keys; }
}
public bool ContainsKey(TKey key)
{
return storage.ContainsKey(key);
}
public IList<TValue> this[TKey key]
{
get
{
if (!storage.ContainsKey(key))
throw new KeyNotFoundException(
string.Format(
"The given key {0} was not found in the collection.", key));
return storage[key];
}
}
}
使い方の簡単な例:
const string key = "supported_encodings";
var map = new MultiMap<string,Encoding>();
map.Add(key, Encoding.ASCII);
map.Add(key, Encoding.UTF8);
map.Add(key, Encoding.Unicode);
foreach (var existingKey in map.Keys)
{
var values = map[existingKey];
Console.WriteLine(string.Join(",", values));
}
元の質問への回答。 Dictionary<string, List<object>>
のようなものは、The Code Project
のMultiMap
というクラスに実装されています。
詳細については、以下のリンクをご覧ください: http://www.codeproject.com/KB/cs/MultiKeyDictionary.aspx
NameValueCollectionは、1つのキー(文字列でもある)の下で複数の文字列値をサポートしていますが、私が知っている唯一の例です。
そのような機能が必要な状況に遭遇したとき、私はあなたの例のような構造を作成する傾向があります。
List<KeyValuePair<string, object>>
オプションを使用する場合、LINQを使用して検索を実行できます。
List<KeyValuePair<string, object>> myList = new List<KeyValuePair<string, object>>();
//fill it here
var q = from a in myList Where a.Key.Equals("somevalue") Select a.Value
if(q.Count() > 0){ //you've got your value }
私の使い方はただ
Dictionary<string, List<string>>
この方法では、文字列のリストを保持する単一のキーがあります。
例:
List<string> value = new List<string>();
if (dictionary.Contains(key)) {
value = dictionary[key];
}
value.Add(newValue);
実際の複製ではなく、合同という意味ですか?そうしないと、ハッシュテーブルは機能しません。
合同とは、2つの別々のキーが同等の値にハッシュできるが、キーが等しくないことを意味します。
たとえば、ハッシュテーブルのハッシュ関数がhashval = key mod 3だったとします。1と4は両方とも1にマッピングされますが、値は異なります。ここで、リストのアイデアが役立ちます。
1を検索する必要がある場合、その値は1にハッシュされ、Key = 1が見つかるまでリストが走査されます。
重複キーの挿入を許可した場合、どのキーがどの値にマッピングされるかを区別することはできません。
@Hector Correaの答えを汎用型の拡張に変更し、カスタムのTryGetValueを追加しました。
public static class ListWithDuplicateExtensions
{
public static void Add<TKey, TValue>(this List<KeyValuePair<TKey, TValue>> collection, TKey key, TValue value)
{
var element = new KeyValuePair<TKey, TValue>(key, value);
collection.Add(element);
}
public static int TryGetValue<TKey, TValue>(this List<KeyValuePair<TKey, TValue>> collection, TKey key, out IEnumerable<TValue> values)
{
values = collection.Where(pair => pair.Key.Equals(key)).Select(pair => pair.Value);
return values.Count();
}
}
私は同じ答えを探してこの投稿を見つけましたが、どれも見つかりませんでしたので、辞書のリストを使用して必要最小限のソリューションを作り上げ、他のすべてのユーザーが指定されたキー(セット)、および値のリストを返します(取得)。
[。
class DKD {
List<Dictionary<string, string>> dictionaries;
public DKD(){
dictionaries = new List<Dictionary<string, string>>();}
public object this[string key]{
get{
string temp;
List<string> valueList = new List<string>();
for (int i = 0; i < dictionaries.Count; i++){
dictionaries[i].TryGetValue(key, out temp);
if (temp == key){
valueList.Add(temp);}}
return valueList;}
set{
for (int i = 0; i < dictionaries.Count; i++){
if (dictionaries[i].ContainsKey(key)){
continue;}
else{
dictionaries[i].Add(key,(string) value);
return;}}
dictionaries.Add(new Dictionary<string, string>());
dictionaries.Last()[key] =(string)value;
}
}
}
これは2つの方法の並行辞書です。
public class HashMapDictionary<T1, T2> : System.Collections.IEnumerable
{
private System.Collections.Concurrent.ConcurrentDictionary<T1, List<T2>> _keyValue = new System.Collections.Concurrent.ConcurrentDictionary<T1, List<T2>>();
private System.Collections.Concurrent.ConcurrentDictionary<T2, List<T1>> _valueKey = new System.Collections.Concurrent.ConcurrentDictionary<T2, List<T1>>();
public ICollection<T1> Keys
{
get
{
return _keyValue.Keys;
}
}
public ICollection<T2> Values
{
get
{
return _valueKey.Keys;
}
}
public int Count
{
get
{
return _keyValue.Count;
}
}
public bool IsReadOnly
{
get
{
return false;
}
}
public List<T2> this[T1 index]
{
get { return _keyValue[index]; }
set { _keyValue[index] = value; }
}
public List<T1> this[T2 index]
{
get { return _valueKey[index]; }
set { _valueKey[index] = value; }
}
public void Add(T1 key, T2 value)
{
lock (this)
{
if (!_keyValue.TryGetValue(key, out List<T2> result))
_keyValue.TryAdd(key, new List<T2>() { value });
else if (!result.Contains(value))
result.Add(value);
if (!_valueKey.TryGetValue(value, out List<T1> result2))
_valueKey.TryAdd(value, new List<T1>() { key });
else if (!result2.Contains(key))
result2.Add(key);
}
}
public bool TryGetValues(T1 key, out List<T2> value)
{
return _keyValue.TryGetValue(key, out value);
}
public bool TryGetKeys(T2 value, out List<T1> key)
{
return _valueKey.TryGetValue(value, out key);
}
public bool ContainsKey(T1 key)
{
return _keyValue.ContainsKey(key);
}
public bool ContainsValue(T2 value)
{
return _valueKey.ContainsKey(value);
}
public void Remove(T1 key)
{
lock (this)
{
if (_keyValue.TryRemove(key, out List<T2> values))
{
foreach (var item in values)
{
var remove2 = _valueKey.TryRemove(item, out List<T1> keys);
}
}
}
}
public void Remove(T2 value)
{
lock (this)
{
if (_valueKey.TryRemove(value, out List<T1> keys))
{
foreach (var item in keys)
{
var remove2 = _keyValue.TryRemove(item, out List<T2> values);
}
}
}
}
public void Clear()
{
_keyValue.Clear();
_valueKey.Clear();
}
IEnumerator IEnumerable.GetEnumerator()
{
return _keyValue.GetEnumerator();
}
}
例:
public class TestA
{
public int MyProperty { get; set; }
}
public class TestB
{
public int MyProperty { get; set; }
}
HashMapDictionary<TestA, TestB> hashMapDictionary = new HashMapDictionary<TestA, TestB>();
var a = new TestA() { MyProperty = 9999 };
var b = new TestB() { MyProperty = 60 };
var b2 = new TestB() { MyProperty = 5 };
hashMapDictionary.Add(a, b);
hashMapDictionary.Add(a, b2);
hashMapDictionary.TryGetValues(a, out List<TestB> result);
foreach (var item in result)
{
//do something
}
私はこの単純なクラスを使用します:
public class ListMap<T,V> : List<KeyValuePair<T, V>>
{
public void Add(T key, V value) {
Add(new KeyValuePair<T, V>(key, value));
}
public List<V> Get(T key) {
return FindAll(p => p.Key.Equals(key)).ConvertAll(p=> p.Value);
}
}
使用法:
var fruits = new ListMap<int, string>();
fruits.Add(1, "Apple");
fruits.Add(1, "orange");
var c = fruits.Get(1).Count; //c = 2;