Frage

Ich verwende ein Dictionary<string, int> bei dem die int ist eine Zählung des Schlüssels.

Jetzt muss ich auf den zuletzt eingefügten Schlüssel im Wörterbuch zugreifen, kenne aber seinen Namen nicht.Der offensichtliche Versuch:

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

funktioniert nicht, weil Dictionary.Keys implementiert keinen []-Indexer.

Ich frage mich nur, ob es eine ähnliche Klasse gibt?Ich habe darüber nachgedacht, einen Stack zu verwenden, aber der speichert nur eine Zeichenfolge.Ich könnte jetzt meine eigene Struktur erstellen und dann eine verwenden Stack<MyStruct>, aber ich frage mich, ob es eine andere Alternative gibt, im Wesentlichen ein Wörterbuch, das einen []-Indexer für die Schlüssel implementiert?

War es hilfreich?

Lösung

Wie @Falanwe in einem Kommentar betont, ist es so, so etwas zu tun falsch:

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

Du sollte nicht hängen von der Reihenfolge der Schlüssel in einem Wörterbuch ab.Wenn Sie eine Bestellung benötigen, sollten Sie eine verwenden Bestelltes Wörterbuch, wie hier vorgeschlagen Antwort.Die anderen Antworten auf dieser Seite sind ebenfalls interessant.

Andere Tipps

Sie können eine verwenden Bestelltes Wörterbuch.

Repräsentiert eine Sammlung von Schlüssel-/Wertpaaren, auf die nach Schlüssel oder Index zugegriffen werden kann.

Ein Wörterbuch ist eine Hash-Tabelle, Sie haben also keine Ahnung von der Reihenfolge der Einfügung!

Wenn Sie den zuletzt eingefügten Schlüssel wissen möchten, würde ich vorschlagen, das Wörterbuch um einen LastKeyInserted-Wert zu erweitern.

Z.B.:

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

}

Bei der Verwendung werden jedoch Probleme auftreten .Remove() Um dieses Problem zu lösen, müssen Sie eine geordnete Liste der eingefügten Schlüssel führen.

Warum erweitern Sie nicht einfach die Wörterbuchklasse, um eine zuletzt eingefügte Schlüsseleigenschaft hinzuzufügen?So etwas wie das Folgende vielleicht?

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

Sie könnten immer Folgendes tun:

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

Aber ich würde es nicht empfehlen.Es gibt keine Garantie dafür, dass sich der zuletzt eingefügte Schlüssel am Ende des Arrays befindet.Die Bestellung der Schlüssel auf MSDN ist nicht spezifiziert und kann geändert werden.In meinem sehr kurzen Test scheint es in der Reihenfolge der Einfügung zu sein, aber Sie sollten besser eine ordnungsgemäße Buchhaltung wie einen Stapel einbauen – wie Sie vorschlagen (obwohl ich auf Ihrer Grundlage keine Notwendigkeit einer Struktur sehe). andere Anweisungen) – oder den Einzelvariablen-Cache, wenn Sie nur den neuesten Schlüssel kennen müssen.

Ich denke, Sie können so etwas tun, die Syntax könnte falsch sein. Seit einiger Zeit habe C# nicht mehr verwendet

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

oder verwenden Sie „Max“ anstelle von „Last“, um den Maximalwert zu erhalten. Ich weiß nicht, welcher besser zu Ihrem Code passt.

Eine Alternative wäre a KeyedCollection wenn der Schlüssel in den Wert eingebettet ist.

Erstellen Sie einfach eine Basisimplementierung in einer versiegelten Klasse, um sie zu verwenden.

Also ersetzen Dictionary<string, int> (was kein sehr gutes Beispiel ist, da es keinen eindeutigen Schlüssel für ein int gibt).

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

Ich stimme dem zweiten Teil von Patricks Antwort zu.Auch wenn in einigen Tests die Einfügereihenfolge beibehalten zu werden scheint, wird in der Dokumentation (und im normalen Verhalten für Wörterbücher und Hashes) ausdrücklich darauf hingewiesen, dass die Reihenfolge nicht angegeben ist.

Sie verlangen nur Ärger, abhängig von der Reihenfolge der Schlüssel.Fügen Sie zur Sicherheit Ihre eigene Buchhaltung hinzu (wie Patrick sagte, nur eine einzige Variable für den zuletzt hinzugefügten Schlüssel).Lassen Sie sich auch nicht von allen Methoden wie Last und Max im Wörterbuch in Versuchung führen, da diese wahrscheinlich mit dem Schlüsselkomparator in Zusammenhang stehen (da bin ich mir nicht sicher).

Falls Sie sich entscheiden, gefährlichen Code zu verwenden, der leicht beschädigt werden kann, ruft diese Erweiterungsfunktion einen Schlüssel von a ab Dictionary<K,V> entsprechend seiner internen Indizierung (die für Mono und .NET derzeit in der gleichen Reihenfolge zu sein scheint, wie Sie sie durch Aufzählung erhalten Keys Eigentum).

Es ist viel besser, Linq zu verwenden: dict.Keys.ElementAt(i), aber diese Funktion iteriert O(N);Das Folgende ist O(1), jedoch mit einer Leistungseinbuße bei der Reflexion.

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

Die Art und Weise, wie Sie die Frage formuliert haben, lässt mich glauben, dass das int im Wörterbuch die „Position“ des Elements im Wörterbuch enthält.Geht man von der Behauptung aus, dass die Schlüssel nicht in der Reihenfolge gespeichert werden, in der sie hinzugefügt werden, würde dies, wenn dies korrekt ist, bedeuten, dass „keys.Count“ (oder „.Count - 1, wenn Sie nullbasiert verwenden)“ weiterhin verwendet werden sollten immer die Nummer des zuletzt eingegebenen Schlüssels sein?

Wenn das richtig ist, gibt es einen Grund, warum Sie nicht stattdessen Dictionary<int, string> verwenden können, damit Sie mydict[ mydict.Keys.Count ] verwenden können?

Ich weiß nicht, ob das funktionieren würde, da ich mir ziemlich sicher bin, dass die Schlüssel nicht in der Reihenfolge gespeichert werden, in der sie hinzugefügt werden, aber Sie könnten die KeysCollection in eine Liste umwandeln und dann den letzten Schlüssel in der Liste abrufen ...aber es wäre einen Blick wert.

Die einzige andere Sache, die mir einfällt, ist, die Schlüssel in einer Nachschlageliste zu speichern und die Schlüssel zur Liste hinzuzufügen, bevor Sie sie dem Wörterbuch hinzufügen ...Es ist allerdings nicht schön.

Um Daniels Beitrag und seine Kommentare zum Schlüssel zu erweitern, könnten Sie auf die Verwendung von a zurückgreifen, da der Schlüssel ohnehin in den Wert eingebettet ist KeyValuePair<TKey, TValue> als Wert.Der Hauptgrund dafür ist, dass der Schlüssel im Allgemeinen nicht unbedingt direkt aus dem Wert ableitbar ist.

Dann würde es so aussehen:

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

Um dies wie im vorherigen Beispiel zu verwenden, würden Sie Folgendes tun:

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;

Sie können auch SortedList und sein generisches Gegenstück verwenden.Diese beiden Klassen und das in Andrew Peters Antwort erwähnte OrderedDictionary sind Wörterbuchklassen, in denen auf Elemente sowohl nach Index (Position) als auch nach Schlüssel zugegriffen werden kann.Informationen zur Verwendung dieser Klassen finden Sie hier: SortedList-Klasse , Generische SortedList-Klasse .

Ein Wörterbuch ist möglicherweise nicht sehr intuitiv für die Verwendung von Indexen als Referenz, aber Sie können ähnliche Vorgänge mit einem Array von durchführen Schlüsselwertpaar:

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

Visual Studios Benutzerstimme gibt einen Link dazu generische OrderedDictionary-Implementierung von dotmore.

Wenn Sie jedoch nur Schlüssel/Wert-Paare nach Index und nicht nach Schlüsseln suchen müssen, können Sie einen einfachen Trick verwenden.Deklarieren Sie eine generische Klasse (ich habe sie ListArray genannt) wie folgt:

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

Sie können es auch mit Konstruktoren deklarieren:

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

Sie lesen beispielsweise einige Schlüssel/Wert-Paare aus einer Datei und möchten sie einfach in der Reihenfolge speichern, in der sie gelesen wurden, um sie später per Index abzurufen:

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

Wie Sie vielleicht bemerkt haben, kann Ihr ListArray nicht unbedingt nur Schlüssel/Wert-Paare enthalten.Die Elementarrays können eine beliebige Länge haben, z. B. in einem Jagged-Array.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top