【发布时间】:2009-09-08 22:42:41
【问题描述】:
我正在寻找类似 Dictionary
是否有一个通用集合,或者我需要使用旧的 .NET 1.1 集合之一?
【问题讨论】:
标签: c# .net data-structures
我正在寻找类似 Dictionary
是否有一个通用集合,或者我需要使用旧的 .NET 1.1 集合之一?
【问题讨论】:
标签: c# .net data-structures
没有。但是,System.Collections.Specialized.OrderedDictionary 应该可以解决大部分需求。
编辑:另一个选择是把它变成一个通用的。我尚未对其进行测试,但它可以编译(C# 6)并且应该可以工作。但是,它仍然具有 Ondrej Petrzilka 在下面的 cmets 中提到的相同限制。
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>。 但是,它具有从值构造键的限制,因此它不是通用的键/值对集合。 (尽管您当然可以像 KeyedCollection<TKey, Tuple<TKey, TItem>> 一样使用它作为解决方法)。
如果您需要它作为IDictionary<TKey, TItem>,它有一个.Dictionary 属性。
我遇到的一个小问题是它是一个抽象类,您必须对其进行子类化并实现:
protected abstract TKey GetKeyForItem(TItem item)
为此,我宁愿只将 lambda 传递给构造函数,但话又说回来,我猜虚拟方法比 lambda 稍快(对此表示赞赏)。
编辑 正如 cmets 中提出的问题:KeyedCollection 保留顺序,因为它继承自 Collection<T>,它确实(它派生自 IList<T>。另请参阅添加方法:将一个对象添加到集合的末尾。)。
【讨论】:
Add 方法的文档:将对象添加到 CollectionGetKeyForItem,这是否意味着列表不能包含重复值?
KeyedCollection 使用 List 来存储它的项目,它继承自 Collection。
base.InsertItem 。不幸的是,除非编辑答案,否则 StackOverflow(相当奇怪)缺乏删除反对票的方法。 :( 无论如何,我已经删除了我不正确的评论。
有一个OrderedDictionary 类,它是一个字典,但可以按插入顺序索引,但它不是泛型的。目前.Net框架中还没有一个通用的。
我从 .Net 团队的某个地方读到了一条评论,说他们可能在未来实现一个通用版本,但如果是这样,它很可能会被称为 IndexableDictionary 而不是OrderedDictionary 使其行为更加明显。
编辑: 找到了引用。它位于 OrderedDictionary 的 MSDN 页面上,归功于 Microsoft 的 David M. Kean:
这种类型实际上是错误命名的;它不是一个“有序”字典,而是一个“索引”字典。虽然,今天没有这种类型的等效通用版本,但如果我们将来添加一个,我们很可能会命名为“IndexedDictionary”类型。
【讨论】:
这是 非泛型 Systems.Collections.Specialized.OrderedDictionary 类型的包装器。
这种类型将按插入顺序返回键/值/对序列,很像 Ruby 2.0 哈希。
它不需要 C#6 魔法,符合IDictionary<TKey,TValue>(这也意味着访问未分配的键会引发异常),并且应该是可序列化的。
在 Adrian 的回答中,每条注释都将其命名为“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(); }
}
}
【讨论】:
IDictionary<TKey, TValue> 的好处是什么?
IDictionary<TKey, TValue> 的地方使用结果对象(考虑到即使ConcurrentDictionary<..> 实现IDictionary<..>)
code project 上有一个通用实现,它带有合理数量的测试用例。
作者选择了一个相当有趣的名字(KeyedList),这使得它很难找到。
【讨论】:
保留插入的通用键/值对的另一个选项是使用类似的东西:
Queue<KeyValuePair<string, string>>
这将是一个有保证的有序列表。您可以在类似于添加/删除字典的有序派系中入队和出队,而不是调整数组的大小。它通常可以作为非调整大小的有序(通过插入)数组和自动调整大小的无序(通过插入)列表之间的中间地带。
【讨论】:
List<>。这种方法的一大缺点是按键查找变得更加复杂。
我知道您正在编写 C#,但 Java 有一个名为 LinkedHashMap 的类,它使用私有 LinkedList 来维护键的插入顺序。如果您找不到合适的通用解决方案,也许这将是实施您自己的解决方案的开始。
【讨论】:
如果您需要Add、Remove、ContainsKey 的恒定复杂性和订单保存,那么 .NET Framework 4.5 中没有这样的通用性。
如果您对 3rd 方代码没问题,请查看我的存储库(许可 MIT 许可证): https://github.com/OndrejPetrzilka/Rock.Collections
有OrderedDictionary<K,V>收藏:
Dictionary<K,V>(来自.NET Core)Dictionary<K,V>具有相同的操作复杂性
Dictionary<K,V> 相比,Add 和 Remove 的操作速度要慢约 20%
【讨论】:
代码:
//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
【讨论】: