webentwicklung-frage-antwort-db.com.de

Doppelte Schlüssel in .NET-Wörterbüchern?

Gibt es in der .NET-Basisklassenbibliothek Wörterbuchklassen, in denen doppelte Schlüssel verwendet werden können? Die einzige Lösung, die ich gefunden habe, ist das Erstellen einer Klasse wie:

Dictionary<string, List<object>>

Dies ist jedoch ziemlich ärgerlich für die Verwendung. In Java glaube ich, dass eine MultiMap dies schafft, aber kein Analog in .NET finden kann.

Wenn Sie .NET 3.5 verwenden, verwenden Sie die Lookup -Klasse.

BEARBEITEN: In der Regel erstellen Sie eine Lookup mit Enumerable.ToLookup . Dies setzt jedoch voraus, dass Sie es nicht nachträglich ändern müssen - aber ich finde normalerweise, dass das gut genug ist.

Wenn das nicht für Sie arbeitet, glaube ich nicht, dass irgendetwas im Framework hilfreich ist - und die Verwendung des Wörterbuchs ist so gut, wie es nur geht :(

209
Jon Skeet

Die List-Klasse funktioniert eigentlich gut für Schlüssel-/Wertesammlungen, die Duplikate enthalten, bei denen Sie die Sammlung durchlaufen möchten. Beispiel:

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

Mit List <KeyValuePair <string, string>> können Sie dies auf eine Weise tun

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

Ausgaben k1 = v1, k1 = v2, k1 = v3

37
Hector Correa

Wenn Sie Strings sowohl als Schlüssel als auch als Werte verwenden, können Sie System.Collections.Specialized.NameValueCollection verwenden, das über die GetValues ​​(String Key) -Methode ein Array von Stringwerten zurückgibt.

21
Matt

Ich bin gerade auf die PowerCollections - Bibliothek gestoßen, die unter anderem eine Klasse namens MultiDictionary enthält. Dies umschließt diese Art von Funktionalität ordentlich.

Sehr wichtiger Hinweis zur Verwendung von Lookup:

Sie können eine Instanz von Lookup(TKey, TElement) erstellen, indem Sie ToLookup für ein Objekt aufrufen, das IEnumerable(T) implementiert.

Es gibt keinen öffentlichen Konstruktor, um eine neue Instanz von Lookup(TKey, TElement) zu erstellen. Darüber hinaus sind Lookup(TKey, TElement)-Objekte unveränderlich, das heißt, Sie können keine Elemente oder Schlüssel zu einem Lookup(TKey, TElement)-Objekt hinzufügen oder entfernen, nachdem es erstellt wurde.

(von MSDN)

Ich denke, das wäre für die meisten Anwendungen ein Show-Stopper.

14
TheSoftwareJedi

Ich denke so etwas wie List<KeyValuePair<object, object>> würde den Job machen.

10
MADMap

Wenn Sie> = .NET 4 verwenden, können Sie Tuple Class verwenden:

// 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

Schauen Sie sich die C5'sHashBag class an.

6
Yuval

Es ist leicht genug, Ihre eigene Version eines Wörterbuchs zu "rollen", die "doppelte Schlüsseleinträge" erlaubt. Hier ist eine grobe einfache Implementierung. Möglicherweise möchten Sie die Unterstützung für die meisten (wenn nicht alle) für IDictionary<T> hinzufügen.

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];
        }
    }
}

Ein schnelles Beispiel zur Verwendung:

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));
}
4
ChristopheD

Auf die ursprüngliche Frage antworten. Etwas wie Dictionary<string, List<object>> ist in der Klasse MultiMap in The Code Project implementiert.

Weitere Informationen finden Sie unter dem folgenden Link: http://www.codeproject.com/KB/cs/MultiKeyDictionary.aspx

4
Dan

Die NameValueCollection unterstützt mehrere Zeichenfolgenwerte unter einem Schlüssel (der auch eine Zeichenfolge ist), ist jedoch das einzige Beispiel, das mir bekannt ist.

Ich neige dazu, Konstrukte zu erstellen, die der in Ihrem Beispiel ähnlich sind, wenn ich auf Situationen stoße, in denen ich diese Art von Funktionalität benötige. 

3
ckramer

Wenn Sie die List<KeyValuePair<string, object>>-Option verwenden, können Sie LINQ für die Suche verwenden:

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

Die Art, wie ich benutze, ist nur eine

Dictionary<string, List<string>>

Auf diese Weise haben Sie einen einzigen Schlüssel, der eine Liste von Strings enthält.

Beispiel:

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

Meinen Sie damit kongruent und nicht wirklich ein Duplikat? Andernfalls könnte eine Hashtabelle nicht funktionieren.

Übereinstimmend bedeutet, dass zwei separate Schlüssel auf den entsprechenden Wert zugreifen können, die Schlüssel sind jedoch nicht gleich.

Beispiel: Angenommen, die Hash-Funktion Ihrer Hashtabelle war nur Hashwert = Schlüsselmod. 3. Sowohl 1 als auch 4 werden auf 1 abgebildet, sind jedoch unterschiedliche Werte. Hier kommt Ihre Vorstellung von einer Liste ins Spiel.

Wenn Sie 1 suchen müssen, wird dieser Wert auf 1 gehasht. Die Liste wird durchlaufen, bis der Schlüssel = 1 gefunden wird.

Wenn Sie das Einfügen doppelter Schlüssel zulassen, können Sie nicht unterscheiden, welche Schlüssel zu welchen Werten gehören.

1

Ich habe die Antwort von @Hector Correa in eine Erweiterung mit generischen Typen geändert und auch einen benutzerdefinierten TryGetValue hinzugefügt.

  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

Ich bin auf der Suche nach der gleichen Antwort auf diesen Beitrag gestoßen und habe keine gefunden. Deshalb baute ich eine Bare-Bones-Beispiellösung mit einer Liste von Wörterbüchern auf und überschrieb den Operator [], um ein neues Wörterbuch zur Liste hinzuzufügen, wenn alle anderen über eine Schlüssel (set) und gibt eine Liste von Werten zurück (get).
Es ist hässlich und ineffizient, es wird NUR mit dem Schlüssel abgerufen/gesetzt und es wird immer eine Liste zurückgegeben, aber es funktioniert:

 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

Dies ist ein paralleles Wörterbuch, das Ihnen helfen wird:

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

beispiele:

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 Yousefie

ich benutze diese einfache Klasse:

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

verwendungszweck:

var fruits = new ListMap<int, string>();
fruits.Add(1, "Apple");
fruits.Add(1, "orange");
var c = fruits.Get(1).Count; //c = 2;
0
John