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

219
22

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


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

спросил(а) 2021-01-25T17:29:30+03:00 4 месяца, 2 недели назад
1
Решение
195

Нет. Однако 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);
}

ответил(а) 2021-01-25T17:29:30+03:00 4 месяца, 2 недели назад
139

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


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


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

protected abstract TKey GetKeyForItem(TItem item)

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

ответил(а) 2021-01-25T17:29:30+03:00 4 месяца, 2 недели назад
133

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


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


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


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


ответил(а) 2021-01-25T17:29:30+03:00 4 месяца, 2 недели назад
99

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

ответил(а) 2021-01-25T17:29:30+03:00 4 месяца, 2 недели назад
89

Существует общая реализация проекта кода, который поставляется с разумным количеством тестовых примеров.


Автор выбрал довольно забавное имя (KeyedList), из-за которого довольно сложно найти.

ответил(а) 2021-01-25T17:29:30+03:00 4 месяца, 2 недели назад
63

Если вам нужна постоянная сложность 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 байт памяти на элемент

ответил(а) 2021-01-25T17:29:30+03:00 4 месяца, 2 недели назад
63

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


Queue<KeyValuePair<string, string>>

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

ответил(а) 2021-01-25T17:29:30+03:00 4 месяца, 2 недели назад
65

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

ответил(а) 2021-01-25T17:29:30+03:00 4 месяца, 2 недели назад
63

Вот обертка для не-общего типа 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(); }
}
}

ответил(а) 2021-01-25T17:29:30+03:00 4 месяца, 2 недели назад
-8

код:


//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

ответил(а) 2021-01-25T17:29:30+03:00 4 месяца, 2 недели назад
Ваш ответ
Введите минимум 50 символов
Чтобы , пожалуйста,
Выберите тему жалобы:

Другая проблема