Pergunta

Existem aulas de dicionário na biblioteca de classes base do .NET que permitem chaves duplicadas para ser usado? A única solução que eu encontrei é criar, por exemplo, uma classe como:

Dictionary<string, List<object>>

Mas isso é bastante irritante para realmente usar. Em Java, eu acredito que uma MultiMap faz isso, mas não consegue encontrar um análogo em .NET.

Foi útil?

Solução

Se você estiver usando o .NET 3.5, use o Lookup classe.

EDIT: Você geralmente criar um Lookup usando Enumerable.ToLookup . Isso supor que você não precisa mudá-lo depois -. Mas eu normalmente acho que é bom o suficiente

Se isto não trabalho para você, eu não acho que haja algo no quadro que ajudará - e usando o dicionário é tão bom quanto ele ganha: (

Outras dicas

A classe Lista realmente funciona muito bem para as coleções de chave / valor que contêm duplicatas onde você gostaria de iteração sobre a coleção. Exemplo:

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

Aqui está uma maneira de fazer isso com List >

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

Saídas k1 = v1, k1 = v2, k1 = v3

Se você estiver usando cordas como as chaves e os valores, você pode usar System.Collections.Specialized.NameValueCollection , que irá retornar uma matriz de valores de cadeia através do método GetValues ??(chave de cadeia).

Eu deparei com a PowerCollections biblioteca que inclui, entre outras coisas, uma classe chamada MultiDictionary. Isso envolve perfeitamente esse tipo de funcionalidade.

Nota muito importante a respeito do uso de pesquisa:

Você pode criar uma instância de uma Lookup(TKey, TElement) chamando ToLookup em um objeto que implementa IEnumerable(T)

Não há construtor público para criar uma nova instância de um Lookup(TKey, TElement). Além disso, objetos Lookup(TKey, TElement) são imutáveis, ou seja, você não pode adicionar ou remover elementos ou chaves de um objeto Lookup(TKey, TElement) após ele ter sido criado.

(a partir de MSDN)

Eu acho que isso seria um empecilho para a maioria dos usos.

Eu acho que algo como List<KeyValuePair<object, object>> iria fazer o trabalho.

Se você estiver usando> = .NET 4 então você pode usar 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());
}

Tenha um olhar em HashBag classe.

É fácil o suficiente para "rolar sua própria" versão de um dicionário que permite entradas "chave duplicada". Aqui está uma implementação simples áspera. Você pode querer considerar a adição de suporte para, basicamente, a maioria (se não todos) em 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];
        }
    }
}

Um exemplo rápido sobre como usá-lo:

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

Em resposta à pergunta inicial. Algo como Dictionary<string, List<object>> é implementado em uma classe chamada MultiMap em O Code Project.

Você pode encontrar mais informações para o link abaixo: http://www.codeproject.com/KB/cs/MultiKeyDictionary.aspx

O NameValueCollection suporta vários valores de cadeia sob uma chave (que também é uma string), mas é o único exemplo que eu estou ciente de.

I tendem a criar construções semelhantes ao de seu exemplo, quando eu me deparo com situações onde eu preciso esse tipo de funcionalidade.

Ao usar a opção List<KeyValuePair<string, object>>, você poderia usar LINQ para fazer a pesquisa:

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 }

A maneira uso I é apenas um

Dictionary<string, List<string>>

Desta forma, você tem uma única tecla segurando uma lista de strings.

Exemplo:

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

Você congruente média e não uma duplicata real? Caso contrário, um hashtable não seria capaz de trabalho.

meios congruentes que duas chaves separadas podem hash para o valor equivalente, mas as chaves não são iguais.

Por exemplo: digamos função hash do seu hashtable era apenas hashval = chave mod 3. Ambos 1 e 4 mapa para 1, mas são valores diferentes. Este é o lugar onde a sua ideia de uma lista entra em jogo.

Quando você precisa para procurar 1, esse valor é hash a 1, a lista é percorrida até o Key = 1 é encontrado.

Se você permitiu chaves duplicadas para ser inserido, você não seria capaz de diferenciar quais teclas mapear para que valores.

me deparei com este post em busca da mesma resposta, e não o achou, então eu montaram um exemplo solução de nu-ossos usando uma lista de dicionários, substituindo o operador [] para adicionar um novo dicionário para a lista quando todos outros têm uma determinada chave (set), e retornar uma lista de valores (get).
É feio e ineficiente, ele só fica / conjuntos por chave, e ele sempre retorna uma lista, mas funciona:

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

Eu mudei resposta @Hector Correa 's em uma extensão com tipos genéricos e também um costume TryGetValue a ele.

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

Este é um dicionário Concurrent tow maneira que eu acho que isso vai ajudá-lo:

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

exemplos:

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
            }

i usar essa classe simples:

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

uso:

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

U pode definir um método para a construção de uma chave corda Composto em todos os lugares u quer usar dicionário u deve usar este método para construir sua chave por exemplo:

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

para usar:

myDict.ContainsKey(keyBuilder(key1, key2))

chaves duplicadas quebrar todo o contrato do dicionário. Em um dicionário cada chave é única e mapeado para um único valor. Se você deseja vincular um objeto a um número arbitrário de objetos adicionais, a melhor aposta pode ser algo semelhante a um DataSet (na linguagem comum a tabela). Coloque suas chaves em uma coluna e os seus valores no outro. Isto é significativamente mais lento do que um dicionário, mas esse é o seu compromisso para perder a capacidade de hash de objetos-chave.

Também isso é possível:

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

Desta forma, podemos ter chaves únicas. Espero que isso funcione para você.

Você pode adicionar mesmas chaves com caixa diferente, como:

key1
key1
KEY1
key1
key1
Key1

Eu sei que é resposta manequim, mas funcionou para mim.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top