.NET数据结构:ArrayList,List,HashTable,Dictionary,SortedList,SortedDictionary —速度,内存以及何时使用它们?


213

.NET具有许多复杂的数据结构。不幸的是,它们中的一些非常相似,而且我并不总是确定何时使用一种以及何时使用另一种。我的大多数C#和Visual Basic书籍都在一定程度上谈论它们,但是它们从未真正涉及任何细节。

Array,ArrayList,List,Hashtable,Dictionary,SortedList和SortedDictionary有什么区别?

哪些是可枚举的(IList-可以执行“ foreach”循环)?哪些使用键/值对(IDict)?

那内存占用呢?插入速度?检索速度?

还有其他值得一提的数据结构吗?

我仍在寻找有关内存使用和速度(Big-O表示法)的更多详细信息。


12
您应该将这个问题分开。您要问的是二十种不同的事物,其中一半可以通过简单的Google搜索来回答。请更具体;当您的问题如此分散时,很难提供帮助。

33
我曾想过将其分解,但意识到有人可能将所有这些答案整合到一个地方。实际上,如果某人可以提出一个可以对所有内容进行分析的表,那么它可能会成为该站点上的绝佳资源。
椒盐脆饼

9
这个问题可以变成维基吗?
BozoJoe 2011年

1
此MSDN文章涵盖了许多此类问题,包括树,图和集,数据结构的广泛检查
Ryan Fisher

1
Ryan,该链接上的文章已有14岁(发布时为12岁)。旁注我上周一直在阅读它们。但它们也不包含更新的技术,因此迫切需要进行更新。以及更多的性能指标和示例。
htm11h

Answers:


156

从我的头顶上:

  • Array*-代表老式的内存阵列-有点像普通type[]阵列的别名。可以列举。不能自动增长。我会假设插入和检索速度非常快。

  • ArrayList-自动增长数组。增加更多的开销。可以枚举,可能比普通数组慢,但仍然非常快。这些在.NET中使用很多

  • List-我的最爱之一-可以与泛型一起使用,因此您可以使用强类型数组,例如List<string>。除此之外,行为非常像ArrayList

  • Hashtable-普通的旧哈希表。O(1)至O(n)最坏的情况。可以枚举值和键属性,并执行键/值对

  • Dictionary -与上述相同,仅通过泛型强类型输入,例如 Dictionary<string, string>

  • SortedList-排序后的通用列表。插入速度很慢,因为它必须弄清楚放置位置。可以枚举。由于不必求助,因此在检索上可能相同,但是删除将比普通的旧列表慢。

我倾向于使用ListDictionary所有的时间-一旦你开始使用它们泛型强类型,它真的很难回到标准的非通用的。

还有许多其他数据结构- KeyValuePair您可以使用它们来做一些有趣的事情,还有一个SortedDictionary也可以使用。


3
哈希表为O(1),最坏的情况(有冲突)可以为O(n)
贾斯汀·博佐尼尔

7
您还需要在这里添加许多其他数据结构。如LinkedList,跳过列表,堆栈,队列,堆,树,图。这些也是非常重要的数据结构。
DarthVader 2011年

2
.Net 4.0中添加的ConcurrentDictionary提供了具有线程安全性的通用字典
Harindaka

2
另外BlockingCollection <T>提供了线程安全的生产者/消费者实现
Harindaka

7
ArrayList使用虚拟方法,但List<T>不使用。ArrayList已在很大程度上被List<T>标准集合和Collection<T>自定义集合的基类所取代。Hashtable已被取代Dictionary<TKey, TValue>。我会建议避免ArrayListHashtable对新代码。
山姆·哈威尔


24

首先,.NET中的所有集合都实现IEnumerable。

其次,由于集合是在框架的2.0版中添加的,因此许多集合都是重复的。

因此,尽管通用集合可能会添加功能,但大多数情况下:

  • List是ArrayList的通用实现。
  • 字典是Hashtable的通用实现

数组是固定大小的集合,您可以更改存储在给定索引处的值。

SortedDictionary是根据密钥排序的IDictionary。SortedList是一个IDictionary,它根据所需的IComparer进行排序。

因此,IDictionary实现(那些支持KeyValuePairs的实现)是:* Hashtable * Dictionary * SortedList * SortedDictionary

.NET 3.5中添加的另一个集合是Hashset。它是一个支持集合操作的集合。

另外,LinkedList是标准的链接列表实现(该列表是一个数组列表,可加快检索速度)。


20

以下是一些适合您的一般提示:

  • 您可以foreach在实现的类型上使用IEnumerableIList本质上是IEnumberablewith CountItem(使用从零开始的索引访问项目)属性。IDictionary另一方面意味着您可以通过任意哈希索引访问项目。

  • ArrayArrayListList全部实施IListDictionarySortedDictionaryHashtable实施IDictionary

  • 如果使用的是.NET 2.0或更高版本,建议您使用上述类型的通用副本。

  • 有关这些类型的各种操作的时间和空间复杂性,应查阅其文档。

  • .NET数据结构位于System.Collections名称空间中。有诸如PowerCollections之类的类型库,它们提供了其他数据结构。

  • 要全面了解数据结构,请查阅诸如CLRS之类的资源。


1
msdn来看,似乎sortedList实现了IDictionnary-不是IList
Haim Bendanan

固定。感谢您的评论。好像SortedList保留了键/值的列表,因此它基本上表示字典的数据。不记得我第一次写答案时这堂课是怎么工作的……
blackwing

9

.NET数据结构:

关于ArrayList和List为什么实际上不同的更多讨论

数组

正如一个用户所说,数组是“老派”集合(是的,数组虽然不是的一部分,但被认为是一个集合System.Collections)。但是,与其他集合(即您在标题中列出的集合)(这里为ArrayList和List(Of T))相比,关于数组的“旧派”是什么?让我们从数组的基础开始。

首先,Microsoft .NET中的数组是“使您可以将几个与逻辑相关的项目视为一个集合的机制”(请参阅​​链接的文章)。那是什么意思?数组按顺序存储各个成员(元素),并以起始地址依次存储在内存中。通过使用数组,我们可以轻松访问从该地址开始的顺序存储的元素。

除此之外,与对101个常见概念进行编程相反,Arrays确实可能非常复杂:

数组可以是一维,多维或交错的(锯齿状的数组值得一读)。数组本身不是动态的:初始化后,大小为n的数组保留足够的空间来容纳n个对象。数组中元素的数量不能增加或减少。Dim _array As Int32() = New Int32(100)在内存块上保留足够的空间,以使数组包含100个Int32基本类型对象(在这种情况下,数组初始化为包含0)。该块的地址返回到_array

根据这篇文章,公共语言规范(CLS)要求所有数组都从零开始。.NET中的数组支持基于非零的数组。但是,这种情况不太常见。由于从零开始的数组的“普遍性”,Microsoft花了很多时间优化其性能。因此,基于零的零维(SZ)数组是“特殊”的-的确是数组的最佳实现(与多维等相对),因为SZ具有用于操纵它们的特定中间语言指令。

数组总是通过引用(作为内存地址)传递的-这是Array难题中要了解的重要部分。当他们进行边界检查(会引发错误)时,也可以在数组上禁用边界检查。

同样,数组的最大障碍是它们无法调整大小。他们具有“固定”的能力。向我们的历史介绍ArrayList和List(Of T):

ArrayList-非泛型列表

最好将ArrayList(以及List(Of T)-尽管有一些关键的区别,在后面进行解释)-最好被认为是集合的下一个补充(广义上)。ArrayList继承自IList(“ ICollection”的后代)接口。ArrayList本身比List 更大,需要更多的开销

IList确实使实现能够将ArrayList视为固定大小的列表(如Arrays);但是,除了ArrayLists增加的其他功能之外,使用固定大小的ArrayList并没有真正的优势,因为在这种情况下ArrayLists(相对于Arrays)明显较慢。

根据我的阅读,ArrayLists不能锯齿:“不支持将多维数组用作元素...”。同样,在ArrayLists的棺材中还有另一个钉子。ArrayList也不是“类型化”的-意思是,在所有内容的下方,ArrayList只是一个动态对象数组:Object[]。在实现ArrayList时,这需要大量装箱(隐式)和拆箱(显式),这又增加了它们的开销。

毫无根据的想法:我想我记得读过或听过我的一位教授的话说,ArrayList只是尝试从Arrays迁移到List-type Collections的混蛋概念子,即曾经对Arrays进行了很大的改进,它们已不再是最佳选择,因为已经对集合进行了进一步的开发

List(Of T):什么是ArrayList成为(并希望成为)

内存使用率的差异非常明显,以至于List(Of Int32)所消耗的内存比包含相同原始类型的ArrayList少56%(在上述绅士的链接演示中为8 MB与19 MB:再次,在此处链接)-尽管这是64位计算机造成的结果。这种差异确实说明了两件事:第一,装箱的Int32类型的“对象”(ArrayList)比纯Int32基本类型(List)大得多。第二(2),由于64位计算机的内部工作,差异是指数级的。

那么,有什么区别,List(Of T)是什么?MSDN定义List(Of T)为“ ...索引可以访问的对象的强类型列表”。这里的重要性是“强类型”位:List(Of T)“识别”类型并将对象存储为它们的类型。因此,an Int32存储为an Int32而不是Object类型。这消除了装箱和拆箱引起的问题。

MSDN指定这种区别仅在存储原始类型而不是引用类型时起作用。差异实际上也确实是大规模的:超过500个元素。更为有趣的是,MSDN文档中写道:“使用List(Of T)类的特定于类型的实现而不是使用ArrayList类对您来说是有利的。”

本质上,List(Of T)是ArrayList,但更好。它是ArrayList的“通用等效项”。像ArrayList一样,它不保证要排序后才能排序(如图)。List(Of T)还具有一些附加功能。



3

他们在智能感知中的表现很好。只需键入System.Collections。System.Collections.Generics(首选),您将获得可用列表和简短描述。


3

哈希表/字典的性能为O(1),这意味着性能不是大小的函数。要知道这一点很重要。

编辑:在实践中,Hashtable / Dictionary <>查找的平均时间复杂度为O(1)。


5
没有“性能”之类的东西。复杂程度取决于操作。例如,如果将n个元素插入Dictionary <>,则由于重新哈希处理,它不会是O(1)。
Ilya Ryzhenkov

2
仅供参考,即使经过重新哈希处理,Dictionary仍为O(1)。考虑一下Dictionary扩展之前的场景。一半的元素(自上次扩展以来添加的元素)将被哈希一次。其余一半将被哈希两次。其余的一半,三倍,等等。对每个元素执行的哈希运算的平均数量将为1 + 1/2 + 1/4 + 1/8 ... = 2。扩展后立即发生的情况基本上是相同的,但是每个元素都被散列了一次额外的时间(因此平均散列计数为3)。所有其他方案都在这些方案之间。
超级猫

3

泛型集合将比非泛型集合表现更好,尤其是在遍历许多项目时。这是因为装箱和拆箱不再发生。


2

有关高频系统交易工程的哈希表与字典的重要说明:线程安全问题

Hashtable是线程安全的,可供多个线程使用。字典公共静态成员是线程安全的,但不保证任何实例成员都是安全的。

因此,Hashtable在这方面仍然是“标准”选择。


这部分是正确的。该Hashtable是安全的,只有一个作家和多个读者同时使用。另一方面,Dictionary只要不同时修改多个读取器就可以安全使用。
布莱恩·梅纳德

绝对是 但是,在交易空间中,我们正在同时读取实时市场数据并运行包含附加条目的分析。这也取决于有多少交易员正在使用该系统-如果只是您,那么这显然无关紧要。
罗布

1
.NET 4.0提供了ConcurrentDictionary <TKey,TValue>
Rob,

1

泛型和非泛型集合之间存在细微和不太细微的差异。他们仅使用不同的基础数据结构。例如,Hashtable保证一个作者很多读者不同步。字典没有。


1

最受欢迎的C#数据结构和集合

  • 数组
  • 数组列表
  • 清单
  • 链表
  • 字典
  • 哈希集
  • 叠放
  • 队列
  • SortedList

C#.NET具有许多不同的数据结构,例如,最常见的数据结构之一是数组。但是C#附带了许多其他基本数据结构。选择要使用的正确数据结构是编写结构合理且高效的程序的一部分。

在本文中,我将介绍内置的C#数据结构,包括C#.NET 3.5中引入的新数据结构。注意,这些数据结构中的许多都适用于其他编程语言。

数组

数组可能是最简单,最常见的数据结构。AC#数组基本上是对象列表。它的定义特征是,所有对象都是相同的类型(在大多数情况下),并且它们有特定数量。数组的性质允许根据元素在列表中的位置(也称为索引)非常快速地访问元素。AC#数组的定义如下:

[object type][] myArray = new [object type][number of elements]

一些例子:

 int[] myIntArray = new int[5];
 int[] myIntArray2 = { 0, 1, 2, 3, 4 };

从上面的示例中可以看到,可以初始化一个没有元素或来自一组现有值的数组。只要适合,将值插入数组就很简单。当元素数量多于数组大小时,该操作将变得昂贵,此时需要扩展数组。这将花费更长的时间,因为所有现有元素都必须复制到更大的新数组中。

数组列表

C#数据结构ArrayList是一个动态数组。这意味着ArrayList可以具有任意数量的对象和任何类型。该数据结构旨在简化将新元素添加到数组中的过程。在幕后,ArrayList是一个数组,每次空间用尽时其大小都会加倍。从长远来看,将内部数组的大小加倍是一种非常有效的策略,可以减少元素复制的数量。我们不会在这里证明这一点。数据结构非常易于使用:

    ArrayList myArrayList = new ArrayList();
    myArrayList.Add(56);
    myArrayList.Add("String");
    myArrayList.Add(new Form());

ArrayList数据结构的缺点是必须将检索到的值强制转换回其原始类型:

int arrayListValue = (int)myArrayList[0]

来源和更多信息,您可以在这里找到


By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.