Pergunta

Eu estou usando um Dictionary<string, int> onde o int é uma contagem da chave.

Agora, o que eu preciso para acessar a última Chave inserida dentro do Dicionário, mas eu não sei o nome dele.A tentativa óbvia:

int LastCount = mydict[mydict.keys[mydict.keys.Count]];

não funciona, porque Dictionary.Keys não implementar uma []-indexador.

Eu só me pergunto se existe algo de semelhante classe?Pensei em utilizar uma Pilha, mas que apenas armazena uma seqüência de caracteres.Agora eu poderia criar a minha própria estrutura e, em seguida, usar um Stack<MyStruct>, mas gostaria de saber se existe outra alternativa, essencialmente, um Dicionário que implementa um []-indexador nas Teclas?

Foi útil?

Solução

Como o @Falanwe pontos em um comentário, fazendo algo parecido com isso é incorreto:

int LastCount = mydict.Keys.ElementAt(mydict.Count -1);

Você não deve dependem da ordem de chaves em um Dicionário.Se você precisa de encomenda, você deve usar um OrderedDictionary, como sugerido neste responder.Outras respostas nesta página são interessantes também.

Outras dicas

Você pode usar um OrderedDictionary.

Representa uma coleção de chave/valor pares que são acessíveis através da tecla ou índice.

Um Dicionário é uma Tabela de Hash, então você não tem idéia de ordem de inserção!

Se você quer saber a última chave inserida eu gostaria de sugerir que prorroga o Dicionário para incluir um LastKeyInserted valor.

E. g.:

public MyDictionary<K, T> : IDictionary<K, T>
{
    private IDictionary<K, T> _InnerDictionary;

    public K LastInsertedKey { get; set; }

    public MyDictionary()
    {
        _InnerDictionary = new Dictionary<K, T>();
    }

    #region Implementation of IDictionary

    public void Add(KeyValuePair<K, T> item)
    {
        _InnerDictionary.Add(item);
        LastInsertedKey = item.Key;

    }

    public void Add(K key, T value)
    {
        _InnerDictionary.Add(key, value);
        LastInsertedKey = key;
    }

    .... rest of IDictionary methods

    #endregion

}

Você vai correr em problemas, no entanto, quando você usa .Remove() por isso, para superar isso, você terá que manter uma lista ordenada das chaves inserido.

Por que você simplesmente não estender o dicionário de classe para adicionar uma chave de última inserido propriedade.Algo como o seguinte, talvez?

public class ExtendedDictionary : Dictionary<string, int>
{
    private int lastKeyInserted = -1;

    public int LastKeyInserted
    {
        get { return lastKeyInserted; }
        set { lastKeyInserted = value; }
    }

    public void AddNew(string s, int i)
    {
        lastKeyInserted = i;

        base.Add(s, i);
    }
}

Você sempre pode fazer isso:

string[] temp = new string[mydict.count];
mydict.Keys.CopyTo(temp, 0)
int LastCount = mydict[temp[mydict.count - 1]]

Mas eu não recomendaria.Não há nenhuma garantia de que a última chave inserida no final da matriz.A ordenação para o Chaves no MSDN não é especificado, e sujeitos a alterações.Na minha breve teste, ele não parece estar em ordem de inserção, mas você seria melhor fora de construção adequada da contabilidade como uma pilha--como você sugere (apesar de eu não vejo a necessidade de uma estrutura com base em outros relatos)--ou a única variável de cache se você só precisa saber a última chave.

Eu acho que você pode fazer algo como isso, a sintaxe pode estar errado, não usado C# em quando Para obter o último item

Dictionary<string, int>.KeyCollection keys = mydict.keys;
string lastKey = keys.Last();

ou usar o Max em vez de Durar para obter o máximo de valor, eu não sei qual deles se encaixa melhor seu código.

Uma alternativa seria um KeyedCollection se a chave estiver incorporado no valor.

Basta criar uma implementação básica de uma classe selada para uso.

Então, para substituir Dictionary<string, int> (o que não é um exemplo muito bom de como não há uma clara chave para um int).

private sealed class IntDictionary : KeyedCollection<string, int>
{
    protected override string GetKeyForItem(int item)
    {
        // The example works better when the value contains the key. It falls down a bit for a dictionary of ints.
        return item.ToString();
    }
}

KeyedCollection<string, int> intCollection = new ClassThatContainsSealedImplementation.IntDictionary();

intCollection.Add(7);

int valueByIndex = intCollection[0];

Eu concordo com a segunda parte do Patrick resposta.Mesmo se em alguns testes, ele parece manter a ordem de inserção, a documentação (e o comportamento normal para dicionários e hashes) afirma explicitamente a ordem é não especificado.

Você está apenas pedindo para ter problemas em função de ordenação das chaves.Adicionar a sua contabilidade própria (como Patrick disse, apenas uma única variável para a última chave adicionado) para ter certeza.Também, não ser tentado por todos os meios, tais como Último e Max no dicionário como essas são, provavelmente, em relação à chave de comparação (eu não tenho certeza sobre isso).

No caso de você decidir usar o código perigoso que está sujeita a quebras, esta extensão, a função de buscar uma chave a partir de uma Dictionary<K,V> de acordo com a sua interno de indexação (que para Mono e .NET, actualmente, parece ser da mesma ordem como você começa por enumerar os Keys propriedade).

É muito preferível a utilização de Linq: dict.Keys.ElementAt(i), mas que função irá iterar O(N);o seguinte é O(1), mas com uma reflexão penalidade de desempenho.

using System;
using System.Collections.Generic;
using System.Reflection;

public static class Extensions
{
    public static TKey KeyByIndex<TKey,TValue>(this Dictionary<TKey, TValue> dict, int idx)
    {
        Type type = typeof(Dictionary<TKey, TValue>);
        FieldInfo info = type.GetField("entries", BindingFlags.NonPublic | BindingFlags.Instance);
        if (info != null)
        {
            // .NET
            Object element = ((Array)info.GetValue(dict)).GetValue(idx);
            return (TKey)element.GetType().GetField("key", BindingFlags.Public | BindingFlags.Instance).GetValue(element);
        }
        // Mono:
        info = type.GetField("keySlots", BindingFlags.NonPublic | BindingFlags.Instance);
        return (TKey)((Array)info.GetValue(dict)).GetValue(idx);
    }
};

A maneira como você formulada a questão que me leva a crer que o int no Dicionário contém o item "posição" no Dicionário.Julgando a partir da afirmação de que as chaves não são armazenados na ordem em que são adicionados, se isso é correto, o que significaria que as chaves.Contagem (ou .Count - 1, se você está utilizando baseado em zero) ainda deve ser sempre o número da última inseridos chave?

Se isso é correto, existe algum motivo você não pode utilizar em vez disso, Dicionário<int, string=""> de modo que você pode usar mydict[ mydict.Chaves.Contagem ]?

Eu não sei se isso iria funcionar porque eu estou bem certo de que as chaves não são armazenados na ordem em que são adicionados, mas você poderia conjurar o KeysCollection para uma Lista e, em seguida, pegar a última chave na lista...mas será que vale a pena ter um olhar.

A única coisa que posso pensar é para armazenar as chaves em uma lista de pesquisa e adicionar as chaves para a lista antes de adicioná-las ao dicionário...não é bonito tho.

Para expandir Daniels post e seus comentários sobre a chave, uma vez que a chave é incorporado dentro do valor de qualquer maneira, você pode recorrer ao uso de um KeyValuePair<TKey, TValue> como o valor.A principal razão para isto é que, em geral, a Chave não está, necessariamente, que podem ser retirados a partir do valor.

Em seguida, ele ficaria assim:

public sealed class CustomDictionary<TKey, TValue>
  : KeyedCollection<TKey, KeyValuePair<TKey, TValue>>
{
  protected override TKey GetKeyForItem(KeyValuePair<TKey, TValue> item)
  {
    return item.Key;
  }
}

Para usar este como no exemplo anterior, você faria:

CustomDictionary<string, int> custDict = new CustomDictionary<string, int>();

custDict.Add(new KeyValuePair<string, int>("key", 7));

int valueByIndex = custDict[0].Value;
int valueByKey = custDict["key"].Value;
string keyByIndex = custDict[0].Key;

Você também pode usar SortedList e Genérico de contrapartida.Estas duas classes e Andrew Peters resposta mencionado OrderedDictionary estão dicionário de classes em que os itens podem ser acessados pelo índice (posição), bem como pela chave.Como usar essas classes que você pode encontrar: Classe SortedList , SortedList Classe Genérica .

Um dicionário pode não ser muito intuitivo para utilizar o índice de referência, mas, você pode ter operações semelhantes com uma matriz de KeyValuePair:

ex.KeyValuePair<string, string>[] filters;

O Visual Studio UserVoice fornece um link para genérico OrderedDictionary implementação por dotmore.

Mas se você só precisa obter pares chave/valor por índice e não precisa ficar em valores de chaves, você pode usar um truque simples.Declarar uma determinada classe genérica (chamei de ListArray) da seguinte maneira:

class ListArray<T> : List<T[]> { }

Você também pode declará-lo com os construtores de:

class ListArray<T> : List<T[]>
{
    public ListArray() : base() { }
    public ListArray(int capacity) : base(capacity) { }
}

Por exemplo, você leia alguns pares de chave/valor a partir de um arquivo e apenas deseja armazená-los na ordem em que foram lidos, assim, levá-los mais tarde por índice:

ListArray<string> settingsRead = new ListArray<string>();
using (var sr = new StreamReader(myFile))
{
    string line;
    while ((line = sr.ReadLine()) != null)
    {
        string[] keyValueStrings = line.Split(separator);
        for (int i = 0; i < keyValueStrings.Length; i++)
            keyValueStrings[i] = keyValueStrings[i].Trim();
        settingsRead.Add(keyValueStrings);
    }
}
// Later you get your key/value strings simply by index
string[] myKeyValueStrings = settingsRead[index];

Como você pode ter notado, você pode não ter, necessariamente, apenas os pares de chave/valor no seu ListArray.O item matrizes podem ser de qualquer comprimento, como na matriz irregulares.

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