web-dev-qa-db-ja.com

.NET辞書でキーが重複していますか?

.NET基本クラスライブラリに、重複キーの使用を許可するディクショナリクラスはありますか?私が見つけた唯一の解決策は、例えば、次のようなクラスを作成することです:

Dictionary<string, List<object>>

しかし、これは実際に使用するのは非常にいらいらします。 Javaでは、MultiMapでこれを実現できますが、.NETでアナログを見つけることはできません。

.NET 3.5を使用している場合は、 Lookup クラスを使用します。

編集:通常、 Enumerable.ToLookup を使用してLookupを作成します。これは、後で変更する必要がないことを前提としていますが、通常はこれで十分であることがわかります。

それがうまくいかない場合、フレームワークに役立つものはないと思います-そして辞書を使うことはそれと同じくらい良いです取得 :(

216
Jon Skeet

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);
}
155
Tawab

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

37
Hector Correa

キーと値の両方として文字列を使用している場合、 System.Collections.Specialized.NameValueCollection を使用できます。これは、GetValues(string key)メソッドを介して文字列値の配列を返します。

21
Matt

とりわけ、MultiDictionaryと呼ばれるクラスを含む PowerCollections ライブラリに出会いました。これは、このタイプの機能をきれいにラップします。

Lookupの使用に関する非常に重要な注意:

Lookup(TKey, TElement)を実装するオブジェクトでToLookupを呼び出すことにより、IEnumerable(T)のインスタンスを作成できます。

Lookup(TKey, TElement)の新しいインスタンスを作成するパブリックコンストラクターはありません。また、Lookup(TKey, TElement)オブジェクトは不変です。つまり、Lookup(TKey, TElement)オブジェクトの作成後は、要素またはキーを追加または削除できません。

(MSDNから)

私はこれがほとんどの用途のショーストッパーになると思います。

14
TheSoftwareJedi

List<KeyValuePair<object, object>>のようなものがジョブを実行すると思います。

10
MADMap

> = .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());
}
8
user915331

C5'sHashBag クラスをご覧ください。

6
Yuval

「重複キー」エントリを許可する辞書の「独自の」バージョンを作成するのは簡単です。大まかな簡単な実装を次に示します。 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));
}
5
ChristopheD

元の質問への回答。 Dictionary<string, List<object>>のようなものは、The Code ProjectMultiMapというクラスに実装されています。

詳細については、以下のリンクをご覧ください: http://www.codeproject.com/KB/cs/MultiKeyDictionary.aspx

4
Dan

NameValueCollectionは、1つのキー(文字列でもある)の下で複数の文字列値をサポートしていますが、私が知っている唯一の例です。

そのような機能が必要な状況に遭遇したとき、私はあなたの例のような構造を作成する傾向があります。

3
ckramer

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 }
3
Greg

私の使い方はただ

Dictionary<string, List<string>>

この方法では、文字列のリストを保持する単一のキーがあります。

例:

List<string> value = new List<string>();
if (dictionary.Contains(key)) {
     value = dictionary[key];
}
value.Add(newValue);
2
Stefan Mielke

実際の複製ではなく、合同という意味ですか?そうしないと、ハッシュテーブルは機能しません。

合同とは、2つの別々のキーが同等の値にハッシュできるが、キーが等しくないことを意味します。

たとえば、ハッシュテーブルのハッシュ関数がhashval = key mod 3だったとします。1と4は両方とも1にマッピングされますが、値は異なります。ここで、リストのアイデアが役立ちます。

1を検索する必要がある場合、その値は1にハッシュされ、Key = 1が見つかるまでリストが走査されます。

重複キーの挿入を許可した場合、どのキーがどの値にマッピングされるかを区別することはできません。

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();
    }
  }
1
kjhf

私は同じ答えを探してこの投稿を見つけましたが、どれも見つかりませんでしたので、辞書のリストを使用して必要最小限のソリューションを作り上げ、他のすべてのユーザーが指定されたキー(セット)、および値のリストを返します(取得)。
[。

 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;
            }
        }
    }
1
Sintrinsic

これは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
            }
0
Ali Yousefi

私はこの単純なクラスを使用します:

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;
0
John