C#关联数组

23

我一直在使用Hashtable,但由于它们的本质,hashtables是无序的,而我需要在添加它们时保持所有内容的顺序(因为我想以相同的顺序取出它们)。例如,如果我执行以下操作:

pages["date"] = new FreeDateControl("Date:", false, true, false);
pages["plaintiff"] = new FreeTextboxControl("Primary Plaintiff:", true, true, false);
pages["loaned"] = new FreeTextboxControl("Amount Loaned:", true, true, false);
pages["witness"] = new FreeTextboxControl("EKFG Witness:", true, true, false);

当我使用foreach时,我希望能按照以下顺序获取:

pages["date"]  
pages["plaintiff"]  
pages["loaned"]  
pages["witness"] 

我该怎么做?


1
按键顺序排序,而不是插入顺序。 - Jon Skeet
3
你为什么要使用哈希表? - Andrew Song
3
为什么不使用List<T>KeyValuePair<Tkey, Tvalue>的组合? - Andrew Song
1
@Jason:如果您已经有了用于插入顺序的单独列表,为什么还要使用SortedList/SortedDictionary并实现比较器呢?更不用说您的答案甚至没有提到这些内容... - Jon Skeet
2
@wowest,这不是一个Java问题... - Malfist
显示剩余12条评论
10个回答

22

好的,我没注意到那个问题。很遗憾没有通用版本可用。实现方式是...一个列表和哈希表,基本上与我的答案一致,但是已经提供在框架中了 :) - Jon Skeet
1
谢谢。缺乏通用实现是不幸的,但并非不可克服。我计划在我的博客上发布我的实现作为免费代码。我希望微软有一天能提供所有集合的通用版本(包括System.Collections.Specialized和System.ComponentModel中的集合)- 但我不抱太大希望。 - LBushkin

12

编辑:LBushkin是对的 - OrderedDictionary 看起来就可以胜任,尽管以非泛型方式实现。很有趣的是有多少专用集合没有泛型等价物:(Malfist将更改接受答案为LBushkin的回答,这是有道理的。)

(我曾经认为...) .NET没有内置的方法来做到这一点。

基本上你需要保留一个 List<string> 和一个 Dictionary<string,FreeTextboxControl>。当你向字典中添加时,将键添加到列表中。然后,您可以遍历列表并按插入顺序查找键。但是,当您删除或替换项目时需要小心。


2
@Malfist:那不是真的。字典是哈希表,根本不会保持它们的顺序。 - mqp
1
正如Ray所指出的那样,SortedDictionary是按键排序的: 表示一个按键排序的键/值对集合。 http://msdn.microsoft.com/zh-cn/library/f7fta44c.aspx - expedient
3
Dictionary<TKey,TValue> 看起来好像保持了顺序,但并不能保证。特别是在你删除条目后再添加更多时是不保证的。如果你只是添加条目,则它往往会保持插入顺序(如果我没记错的话)- 但这并不是绝对保证的。 - Jon Skeet
我相信.NET提供了OrderedDictionary非泛型类,就是为了这个原因。请查看:http://msdn.microsoft.com/en-us/library/system.collections.specialized.ordereddictionary.aspx - LBushkin
@Jon:文档有误导性,但如果你写一个示例,你会发现它确实按照你直觉的预期行为 - 实际上,D将是最后一项,而不是第一项。 - LBushkin
显示剩余8条评论

0

在.NET 4.0之前没有完美的解决方案。在<3.5中,您可以:

使用具有整数键类型和值类型为项的最派生公共类型的通用SortedList。定义一个整数值(比如i),并在将每个项添加到SortedList时,使键i++,随着您的操作递增它的值。稍后,迭代排序列表的GetValueList属性。这个IList属性将按您放置它们的顺序产生对象,因为它们将按您使用的键进行排序。

这不是非常快,但相当好且通用。如果您还想通过键访问,您需要做其他事情,但我没有在您的要求中看到。如果您不需要通过键检索,并且按键顺序添加项目,以便集合实际上不必执行其排序,则这就是它。

在.NET 4.0中,你将拥有泛型T的SortedSet,这对你来说绝对是完美的选择。没有任何妥协。

那种 SortedList 相对于一开始就使用键/值对列表有什么优势呢? - Jon Skeet
(a.) 这非常简单明了。 (b.) 只要顺序保持不变,排序列表是最优的。如果键需要重新排序,则会失去其效率优势,但在这种情况下并不会发生。 - Patrick Karcher
这个问题如何才能变得简单或最优呢?你仍然需要能够通过原始键获取数据,如果你决定使用插入顺序作为 SortedList/SortedDictionary 的键,那么这将会很棘手。也许,如果你能提供一个完整的示例,既可以通过原始键(如“日期”,“原告”等)高效地访问,又可以按顺序访问,那将会澄清事情…… - Jon Skeet
引用问题中的话,他想要: (a)“在添加它们时保持一切有序” (b)“因为我想按相同的顺序取出它们” (c)“当我使用foreach时” 我将这些视为要求。我并没有尝试允许通过键访问。如果他还想通过键访问,那肯定会改变事情。 - Patrick Karcher
任何类名中带有“Sorted”的类几乎肯定不适用于此目的。 Q指的是维护插入顺序,而不是某种排序顺序。具体来说,SortedListSortedSet对此并没有用处。更基本的List(Of T)可以很好地维护插入顺序[它不是关联数组,但与维护插入顺序相比,它比Sorted类更合适-这里提到的类与List无关]。此外,问题标题和详细信息显示需要按键访问-SortedSet不是“关联数组”。 - ToolmakerSteve

0

我认为使用排序列表可以解决你的问题,因为SortedList对象在内部维护两个数组来存储列表的元素;即一个用于键,另一个用于相关值。每个元素都是一个键/值对,可以作为DictionaryEntry对象访问。

SortedList sl = new SortedList();

foreach(DictionaryEntry x in sl) {}


0

KeyedCollection不能保证维护插入顺序。 - ToolmakerSteve


0
一种替代方案是将有序的键值保留在类似于列表的有序结构中,而其余部分仍存储在字典中。
然后,当您需要访问数据时,只需浏览已排序的列表并沿途查询字典即可。

0

最好的方法是使用C#索引器。它可以配置为我们喜欢的任何内容。我们可以传递intenumlongdouble或任何我们喜欢的东西。

只需创建一个类并给它索引器,配置输入和输出参数即可。这需要更多的工作,但我认为这是唯一正确的方法。

请参阅MSDN链接以获取更多有关如何使用它的信息。


-1

请查看其他已排序列表的建议。 - Andrew Song

-1
正如Haxelit所建议的那样,您可以从KeyedCollection<TKey, TValue>派生。它实际上在达到某个阈值之前使用List,然后同时维护List和Dictionary。如果您可以使用函数从一个值中派生出一个键,那么这是一个简单的解决方案。如果不能,那么就会变得非常混乱。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接