Question

Existe-t-il des classes de dictionnaire dans la bibliothèque de classes de base .NET qui permettent d'utiliser des clés en double ?La seule solution que j'ai trouvée est de créer, par exemple, une classe comme :

Dictionary<string, List<object>>

Mais c’est assez irritant à utiliser.En Java, je crois qu'un MultiMap accomplit cela, mais je ne trouve pas d'analogue dans .NET.

Était-ce utile?

La solution

Si vous utilisez .NET 3.5, utilisez le Lookup classe.

EDIT: vous créez généralement un Enumerable.ToLookup en utilisant <=> . Cela suppose que vous n'avez pas besoin de le changer après coup - mais je trouve généralement que c'est assez bien.

Si cela ne ne fonctionne pas pour vous, je ne pense pas que rien dans le cadre permette de l'aider - et utiliser le dictionnaire est aussi efficace qu'il en obtient: (

Autres conseils

La classe List fonctionne plutôt bien pour les collections de clés / valeurs contenant des doublons pour lesquelles vous souhaitez effectuer une itération sur la collection. Exemple:

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

Voici une façon de procéder avec List < KeyValuePair & Lt; chaîne, chaîne > >

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

Sorties k1 = v1, k1 = v2, k1 = v3

Si vous utilisez des chaînes à la fois comme clés et comme valeurs, vous pouvez utiliser System.Collections.Specialized.NameValueCollection, qui renverra un tableau de valeurs de chaîne via la méthode GetValues(string key).

Je viens de tomber sur la bibliothèque PowerCollections qui comprend, entre autres, une classe appelée MultiDictionary. Cela enveloppe parfaitement ce type de fonctionnalité.

Remarque très importante concernant l’utilisation de Lookup:

Vous pouvez créer une instance d'un Lookup(TKey, TElement) en appelant ToLookup sur un objet implémentant IEnumerable(T)

.

Il n'y a pas de constructeur public pour créer une nouvelle instance d'un <=>. De plus, les <=> objets sont immuables, c'est-à-dire que vous ne pouvez pas ajouter ou supprimer des éléments ou des clés d'un objet <=> après sa création.

(à partir de MSDN)

Je pense que ce serait un spectacle pour la plupart des utilisations.

Je pense que quelque chose comme List<KeyValuePair<object, object>> ferait l'affaire.

Si vous utilisez > = .NET 4, vous pouvez utiliser Tuple Classe:

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

Consultez C5 HashBag class.

Il est assez facile de & "Rouler votre propre &"; version d'un dictionnaire autorisant " dupliquer la clé " les entrées. Voici une mise en œuvre simple et approximative. Vous voudrez peut-être envisager de prendre en charge la plupart (sinon la totalité) sur 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];
        }
    }
}

Un exemple rapide sur son utilisation:

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

En réponse à la question initiale. Quelque chose comme Dictionary<string, List<object>> est implémenté dans une classe appelée MultiMap dans la Code Project.

Vous pouvez trouver plus d’informations sur le lien ci-dessous: http://www.codeproject.com/KB/cs/MultiKeyDictionary.aspx

NameValueCollection prend en charge plusieurs valeurs de chaîne sous une clé (qui est également une chaîne), mais c'est le seul exemple que je connaisse.

J'ai tendance à créer des constructions similaires à celles de votre exemple lorsque je rencontre des situations dans lesquelles j'ai besoin de ce type de fonctionnalité.

Lorsque vous utilisez l'option List<KeyValuePair<string, object>>, vous pouvez utiliser LINQ pour effectuer la recherche:

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 }

La façon dont j'utilise est juste un

Dictionary<string, List<string>>

De cette façon, vous avez une seule clé contenant une liste de chaînes.

Exemple:

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

Voulez-vous dire congruente et non une duplication réelle? Sinon, une table de hachage ne pourrait pas fonctionner.

Congruent signifie que deux clés distinctes peuvent avoir un hachage équivalent, mais que les clés ne sont pas égales.

Par exemple: supposons que la fonction de hachage de votre hashtable soit juste hashval = key mod 3. Les deux valeurs 1 et 4 correspondent à 1, mais sont différentes. C’est là que votre idée d’une liste entre en jeu.

Lorsque vous devez rechercher 1, cette valeur est hachée à 1, la liste est parcourue jusqu'à ce que la clé = 1 soit trouvée.

Si vous autorisiez l'insertion de clés dupliquées, vous ne seriez pas en mesure de différencier les clés en fonction des valeurs.

Je suis tombé sur ce message à la recherche de la même réponse, mais je n’en ai trouvé aucun; j’ai donc construit un exemple simple de solution à l’aide d’une liste de dictionnaires, en remplaçant l’opérateur [] pour ajouter un nouveau dictionnaire à la liste lorsque tous d'autres ont une clé donnée et retournent une liste de valeurs (get).
C'est moche et inefficace, il obtient / définit UNIQUEMENT par clé et renvoie toujours une liste, mais cela fonctionne:

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

J'ai transformé la réponse de @Hector Correa en une extension avec des types génériques et y ai également ajouté une TryGetValue personnalisée.

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

Il s’agit d’un dictionnaire simultané très utile, je pense que cela vous aidera:

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

exemples:

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
            }

J'utilise cette classe simple:

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

utilisation:

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

U peut définir une méthode pour construire une clé de chaîne composée. partout où vous voulez utiliser le dictionnaire u devez utiliser cette méthode pour construire votre clé par exemple:

private string keyBuilder(int key1, int key2)
{
    return string.Format("{0}/{1}", key1, key2);
}

pour utiliser:

myDict.ContainsKey(keyBuilder(key1, key2))

Des clés en double rompent l'intégralité du contrat du dictionnaire. Dans un dictionnaire, chaque clé est unique et mappée sur une valeur unique. Si vous souhaitez lier un objet à un nombre arbitraire d'objets supplémentaires, le meilleur choix peut ressembler à un DataSet (en langage courant une table). Mettez vos clés dans une colonne et vos valeurs dans l'autre. C'est beaucoup plus lent qu'un dictionnaire, mais c'est votre compromis pour perdre la possibilité de hacher les objets clés.

Cela est également possible:

Dictionary<string, string[]> previousAnswers = null;

De cette façon, nous pouvons avoir des clés uniques. J'espère que cela fonctionne pour vous.

Vous pouvez ajouter les mêmes clés avec des cas différents, par exemple:

key1
Key1
KEY1
KeY1
kEy1
keY1

Je sais que c'est une réponse fictive, mais a fonctionné pour moi.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top