Общий набор ключей/ценностей, в котором сохраняется порядок вставки?

Я ищу что-то вроде словаря < K, V > однако с гарантией, что он сохранит порядок вставки. Поскольку словарь является хэш-таблицей, я не думаю, что это так.

Есть ли общий набор для этого или мне нужно использовать одну из старых коллекций .NET 1.1?

Ответ 1

Нет. Однако System.Collections.Specialized.OrderedDictionary должен решить большую потребность в нем.

EDIT: Другой вариант - превратить это в общий. Я не тестировал его, но он компилирует (С# 6) и должен работать. Тем не менее, он все равно будет иметь те же ограничения, о которых упоминает Ondrej Petrzilka в комментариях ниже.

    public class OrderdDictionary<T, K>
    {
        public OrderedDictionary UnderlyingCollection { get; } = new OrderedDictionary();

        public K this[T key]
        {
            get
            {
                return (K)UnderlyingCollection[key];
            }
            set
            {
                UnderlyingCollection[key] = value;
            }
        }

        public K this[int index]
        {
            get
            {
                return (K)UnderlyingCollection[index];
            }
            set
            {
                UnderlyingCollection[index] = value;
            }
        }
        public ICollection<T> Keys => UnderlyingCollection.Keys.OfType<T>().ToList();
        public ICollection<K> Values => UnderlyingCollection.Values.OfType<K>().ToList();
        public bool IsReadOnly => UnderlyingCollection.IsReadOnly;
        public int Count => UnderlyingCollection.Count;
        public IDictionaryEnumerator GetEnumerator() => UnderlyingCollection.GetEnumerator();
        public void Insert(int index, T key, K value) => UnderlyingCollection.Insert(index, key, value);
        public void RemoveAt(int index) => UnderlyingCollection.RemoveAt(index);
        public bool Contains(T key) => UnderlyingCollection.Contains(key);
        public void Add(T key, K value) => UnderlyingCollection.Add(key, value);
        public void Clear() => UnderlyingCollection.Clear();
        public void Remove(T key) => UnderlyingCollection.Remove(key);
        public void CopyTo(Array array, int index) => UnderlyingCollection.CopyTo(array, index);
    }

Ответ 2

На самом деле есть один, который является общим и был вокруг с .net 2.0. Он называется KeyedCollection<TKey, TItem>. Однако у него есть ограничение на то, что он хочет построить ключи из значений, поэтому он не является общей коллекцией пар ключ/значение.

Если вам это нужно как IDictionary<TKey, TItem>, оно имеет свойство .Dictionary.

Несколько второстепенная проблема, с которой я связан, заключается в том, что это абстрактный класс, и вы должны подклассифицировать его и реализовать:

protected abstract TKey GetKeyForItem(TItem item)

Я хотел бы просто передать лямбда в конструктор для этой цели, но опять же, я думаю, виртуальный метод немного быстрее, чем лямбда (любые комментарии по этому поводу оцениваются).

Ответ 3

Существует класс OrderedDictionary, который является словарем, но может быть проиндексирован в порядке вставки, но он не генерируется. В настоящее время в инфраструктуре .Net не существует обобщенного.

Я прочитал комментарий где-то от кого-то из команды .Net, в котором говорится, что в будущем они могут реализовать расширенную версию, но если это так, то скорее всего она будет называться IndexableDictionary вместо OrderedDictionary, чтобы сделать ее поведение более очевидным.

EDIT: нашел цитату. Это было на странице MSDN для OrderedDictionary, приписываемой Дэвиду М. Кину из Microsoft:

Этот тип фактически неверно назван; это не "упорядоченный" словарь как таковой, а скорее "индексированный" словарь. Хотя сегодня нет эквивалентной родовой версии этого типа, если мы добавим ее в будущем, вероятно, мы назовем такой тип "IndexedDictionary".

Ответ 4

Ну, вы можете использовать List<KeyValuePair<K,V>>, который сохранил бы порядок... однако вы потеряли бы функциональность поиска словаря. Зачем вам нужно сохранить заказ?

Ответ 6

Я знаю, что вы пишете С#, но Java имеет класс LinkedHashMap, который использует частный LinkedList для поддержания порядка вставки ключей. Если вы не можете найти подходящее общее решение, возможно, это станет началом реализации вашего собственного.

Ответ 7

Другим вариантом пары Generic Key/Value, которая сохраняет вставку, является использование следующего:

Queue<KeyValuePair<string, string>>

Это будет гарантированный упорядоченный список. Вы можете en-queue и dequeue в упорядоченной фракции, аналогичной Add/Remove of dictionary, а не изменять размер массива. Он часто может выступать в качестве промежуточной основы между упорядоченным (по вставке) массивом без изменения размера и списком неупорядоченных (по вставке) авторезистов.

Ответ 8

Если вам нужна постоянная сложность Add, Remove, ContainsKey и сохранения порядка, тогда в .NET Framework 4.5 нет такого рода.

Если вы в порядке со сторонним кодом, взгляните на мой репозиторий (разрешающая лицензия MIT): https://github.com/OndrejPetrzilka/Rock.Collections

Там OrderedDictionary<K,V> коллекция:

  • исходный код на основе классического Dictionary<K,V> (из .NET Core)
  • сохраняет порядок вставки и позволяет ручное переупорядочение
  • функции обратное перечисление
  • имеет те же операционные сложности, что и Dictionary<K,V> Операции
  • Add и Remove на 20% медленнее по сравнению с Dictionary<K,V>
  • потребляет еще 8 байт памяти на элемент

Ответ 9

Вот обертка для не-общего типа Systems.Collections.Specialized.OrderedDictionary.

Этот тип будет возвращать последовательности ключей/значений/пар в порядке вставки, так же как хеши Ruby 2.0.

Он не требует магии С# 6, соответствует IDictionary<TKey,TValue> (что также означает, что доступ к не назначенному ключу генерирует исключение) и должен быть сериализуемым.

Дано имя "IndexedDictionary" в примечании к ответу Адриана.

YMMV.

using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;

/// <summary>
/// A dictionary that maintains insertion ordering of keys.
/// 
/// This is useful for emitting JSON where it is preferable to keep the key ordering
/// for various human-friendlier reasons.
/// 
/// There is no support to manually re-order keys or to access keys
/// by index without using Keys/Values or the Enumerator (eg).
/// </summary>
[Serializable]
public sealed class IndexedDictionary<TKey, TValue> : IDictionary<TKey, TValue>
{
    // Non-generic version only in .NET 4.5
    private readonly OrderedDictionary _backing = new OrderedDictionary();

    private IEnumerable<KeyValuePair<TKey, TValue>> KeyValuePairs
    {
        get
        {
            return _backing.OfType<DictionaryEntry>()
                .Select(e => new KeyValuePair<TKey, TValue>((TKey)e.Key, (TValue)e.Value));
        }
    }

    public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
    {
        return KeyValuePairs.GetEnumerator();
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }

    public void Add(KeyValuePair<TKey, TValue> item)
    {
        _backing[item.Key] = item.Value;
    }

    public void Clear()
    {
        _backing.Clear();
    }

    public bool Contains(KeyValuePair<TKey, TValue> item)
    {
        return _backing.Contains(item.Key);
    }

    public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex)
    {
        KeyValuePairs.ToList().CopyTo(array, arrayIndex);
    }

    public bool Remove(KeyValuePair<TKey, TValue> item)
    {
        TValue value;
        if (TryGetValue(item.Key, out value)
            && Equals(value, item.Value))
        {
            Remove(item.Key);
            return true;
        }
        return false;
    }

    public int Count
    {
        get { return _backing.Count; }
    }

    public bool IsReadOnly
    {
        get { return _backing.IsReadOnly; }
    }

    public bool ContainsKey(TKey key)
    {
        return _backing.Contains(key);
    }

    public void Add(TKey key, TValue value)
    {
        _backing.Add(key, value);
    }

    public bool Remove(TKey key)
    {
        var result = _backing.Contains(key);
        if (result) {
            _backing.Remove(key);
        }
        return result;
    }

    public bool TryGetValue(TKey key, out TValue value)
    {
        object foundValue;
        if ((foundValue = _backing[key]) != null
            || _backing.Contains(key))
        {
            // Either found with a non-null value, or contained value is null.
            value = (TValue)foundValue;
            return true;
        }
        value = default(TValue);
        return false;
    }

    public TValue this[TKey key]
    {
        get
        {
            TValue value;
            if (TryGetValue(key, out value))
                return value;
            throw new KeyNotFoundException();
        }
        set { _backing[key] = value; }
    }

    public ICollection<TKey> Keys
    {
        get { return _backing.Keys.OfType<TKey>().ToList(); }
    }

    public ICollection<TValue> Values
    {
        get { return _backing.Values.OfType<TValue>().ToList(); }
    }
}

Ответ 10

код:

//A SortedDictionary is sorted on the key (not value)
System.Collections.Generic.SortedDictionary<string, string> testSortDic = new SortedDictionary<string, string>();

//Add some values with the keys out of order
testSortDic.Add("key5", "value 1");
testSortDic.Add("key3", "value 2");
testSortDic.Add("key2", "value 3");
testSortDic.Add("key4", "value 4");
testSortDic.Add("key1", "value 5"); 

//Display the elements.  
foreach (KeyValuePair<string, string> kvp in testSortDic)
{
    Console.WriteLine("Key = {0}, value = {1}", kvp.Key, kvp.Value);
}

Вывод:

Key = key1, value = value 5
Key = key2, value = value 3
Key = key3, value = value 2
Key = key4, value = value 4
Key = key5, value = value 1