Я ищу что-то вроде словаря < K, V > однако с гарантией, что он сохранит порядок вставки. Поскольку словарь является хэш-таблицей, я не думаю, что это так.
Есть ли общий набор для этого или мне нужно использовать одну из старых коллекций .NET 1.1?
Я ищу что-то вроде словаря < K, V > однако с гарантией, что он сохранит порядок вставки. Поскольку словарь является хэш-таблицей, я не думаю, что это так.
Есть ли общий набор для этого или мне нужно использовать одну из старых коллекций .NET 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);
}
На самом деле есть один, который является общим и был вокруг с .net 2.0. Он называется KeyedCollection<TKey, TItem>
. Однако у него есть ограничение на то, что он хочет построить ключи из значений, поэтому он не является общей коллекцией пар ключ/значение.
Если вам это нужно как IDictionary<TKey, TItem>
, оно имеет свойство .Dictionary
.
Несколько второстепенная проблема, с которой я связан, заключается в том, что это абстрактный класс, и вы должны подклассифицировать его и реализовать:
protected abstract TKey GetKeyForItem(TItem item)
Я хотел бы просто передать лямбда в конструктор для этой цели, но опять же, я думаю, виртуальный метод немного быстрее, чем лямбда (любые комментарии по этому поводу оцениваются).
Существует класс OrderedDictionary, который является словарем, но может быть проиндексирован в порядке вставки, но он не генерируется. В настоящее время в инфраструктуре .Net не существует обобщенного.
Я прочитал комментарий где-то от кого-то из команды .Net, в котором говорится, что в будущем они могут реализовать расширенную версию, но если это так, то скорее всего она будет называться IndexableDictionary
вместо OrderedDictionary
, чтобы сделать ее поведение более очевидным.
EDIT: нашел цитату. Это было на странице MSDN для OrderedDictionary
, приписываемой Дэвиду М. Кину из Microsoft:
Этот тип фактически неверно назван; это не "упорядоченный" словарь как таковой, а скорее "индексированный" словарь. Хотя сегодня нет эквивалентной родовой версии этого типа, если мы добавим ее в будущем, вероятно, мы назовем такой тип "IndexedDictionary".
Ну, вы можете использовать List<KeyValuePair<K,V>>
, который сохранил бы порядок... однако вы потеряли бы функциональность поиска словаря. Зачем вам нужно сохранить заказ?
Существует общая реализация проекта кода, который поставляется с разумным количеством тестовых примеров.
Автор выбрал довольно забавное имя (KeyedList), из-за которого довольно сложно найти.
Я знаю, что вы пишете С#, но Java имеет класс LinkedHashMap, который использует частный LinkedList для поддержания порядка вставки ключей. Если вы не можете найти подходящее общее решение, возможно, это станет началом реализации вашего собственного.
Другим вариантом пары Generic Key/Value, которая сохраняет вставку, является использование следующего:
Queue<KeyValuePair<string, string>>
Это будет гарантированный упорядоченный список. Вы можете en-queue и dequeue в упорядоченной фракции, аналогичной Add/Remove of dictionary, а не изменять размер массива. Он часто может выступать в качестве промежуточной основы между упорядоченным (по вставке) массивом без изменения размера и списком неупорядоченных (по вставке) авторезистов.
Если вам нужна постоянная сложность 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>
Вот обертка для не-общего типа 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(); }
}
}
код:
//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