.NET中的数据结构——表

  .NET 是一个很好的开发平台,它提供了大量的非常有用的类库,大大方便了开发人员的效率。其中的集合类就是开发过程当中必不可少的一部分,虽然用了挺长一段时间了,但是都没有认真的研究过它们的实现方式,以及它们分别属于哪种数据结构。只有了解了上述的问题,我们才能更好的在日常的开发当中更有效的使用它们,所以最近使用 ILSpy 反编译软件好好学习了一下.NET 中的集合类,现在将自己学习的心得总结一下,希望对大家有帮助,有不正确的地方也请大家帮助纠正。

表的数组实现

各类型一维数组(int[]long[]等)


概述

       它们实现了一个长度固定的表结构,它的元素必须储存在一个连续的内存空间中。


功能说明

    它提供了表的创建、清除、复制和元素的存取、排序和搜索功能,因为它的长度是固定,所以它不能进行元素的新增和删除。上述功能中的很大一部分功能都是由 Array 类的静态方法提供的,只有少量功能是由数组的实例方法提供的。

表的创建:初始化一个一维数组。

表的清除:由 Array类的静态方法实现,将表中的指定范围内的元素清空,重置为初始值。

表的复制:由 Array类的静态方法实现的浅度复制,将源表中指定范围内的元素复制到目标表中指定的位置;

由实例方法实现的浅复制,其内部还是通过静态方法来实现数组的复制功能。

元素的搜索:由 Array类的静态方法实现。可以正向搜索,也可以反向搜索;可以搜索指定元素在数组中的位置,也可以搜索指定的元素;可以顺序搜索,也可以用二分法搜索(使用二分法搜索时表中的元素必须以排序)。

元素的排序:由Array类的静态方法实现,使用快速排序法将数组元素进行重新排列,以便能够使用二分法进行搜索。

元素的存取:由实例方法实现。使用元素在表中的位置通过下标访问符或 GetValueSetValue 方法对表元素进行随机存取。

 

性能说明

元素的存取:我们只能使用元素在表中的位置通过下标访问符或 GetValueSetValue方法来对元素进行随机存取,所以该操作的时间复杂度为O(1)。但使用GetValue  SetValue 方法来进行随机存取时,有可能会出现装箱的额外操作,因为它们都只操作 object 类型的数据,所以尽量使用下标访问符来进行随机存取,以提高性能(对于引用类型没有差别)。

元素的排序:使用快速排序法进行排序,该操作的时间复杂度为 O(N * log N)

元素的搜索:元素的搜索分为两种情况:顺序搜索和二分搜索。如果使用顺序搜索则该操作的时间复杂度为 O(N);如果使用二分搜索则该操作的时间复杂度为 O(log N)

 

适用场景

       当我们要操作的数据满足下面条件时就比较适合使用数组:

1、  元素数量固定,不需要动态的添加和删除元素;

2、  能够通过元素在表中的位置进行随机存取;


 

ArrayListNamespace: System.CollectionAssemblymscorlib

 

概述

       它实现了一个可以动态添加、删除元素的表结构。实际上,它是通过内部维护一个object 类型的数组 _items 来存放所有的元素,所以它也只提供通过元素的位置存取元素的方式。

 

功能说明

       它提供了表的创建、清除、复制,表元素的存取、搜索、新增、删除和排序功能。

表的创建:实例化 _items,并可以将传入的元素集合拷贝(浅度拷贝)到 _items 中。

表的清除(Clear):清除 _items 中的数据,通过调用 Array.Clear 方法来实现的。

表的复制(CopyTo):将表中指定范围内的元素复制到目标表中指定的位置,通过调用 Array.Copy 方法来实现的。

元素的存取(this[]):只能使用元素在表中的位置通过索引器来对元素进行随机存取。

元素的新增:可以将一个新元素添加到表的最后(Add),也可以将其插入到指定的位置(Insert);可以将一组新元素添加到表的最后(AddRange),也可以将其插入到指定的位置(InsertRange)。

元素的删除:可以从表中删除指定的元素(Remove),也可以将表中指定位置的元素删除(RemoveAt),还可以将表中指定范围内的元素删除(RemoveRange)。

元素的排序(Sort):将表中的元素进行快速排序,以便能够使用二分法进行元素的搜索。

元素的搜索:可以进行顺序搜索(IndexOf  LastIndexOf,通过调用Array.IndexOfArray.LastIndexOf方法实现);也可以使用二分法进行搜索(BinarySearch,调用Array.BinarySearch 方法实现)。

 

性能说明

元素的存取:虽然对于外部来说是使用索引器来对元素进行随机存取,其实其内部就是使用数组的下标对_items 的元素进行存取,所以该操作的时间复杂度为O(1)

元素的新增:虽然对于外部来说它提供了元素的新增功能,但是对于其内部来说只是指定数组元素的赋值。

                   如果在表的尾部添加元素(Add),则只是简单的为指定的数组元素赋值,所以该操作的时间复杂度为O(1);如果在表的中间插入元素(Insert),则需要将数组中指定位置之后的元素都向后移动,然后为指定位置的数组元素赋以新值,则该操作的时间复杂度为O(N)

                   只要存在元素的新增,这里就会存在一个扩容的问题。ArrayList内部是通过创建容量更大的新的数组,将所有已有元素拷贝到新数组中指定的位置,然后再将新增的元素存入数组。

元素的删除:当表中指定的元素被删除时,需要将 _items 中被删除元素之后的元素都向前移动,即 _items 中的所有元素必须集中在数组的前部,中间不能出现空元素,所以该操作的时间复杂度为 O(N)

元素的排序:使用快速排序法对表中的元素进行重新排序,该操作的时间复杂度为 O(N * log N)

元素的搜索:如果使用顺序搜索则该操作的时间复杂度 O(N);如果表元素已经进行过排序,使用二分法搜索则该操作的时间复杂度为 O(log N)

 

适用场景

    当我们要操作的数据满足下面条件时就比较适合使用ArrayList

1、  能够通过元素在表中的位置进行随机存取;

2、  能够动态新增和删除元素;

3、  不能保证各个元素的数据类型能够兼容;

4、  能够对元素进行排序。


 

List<T>NamespaceSystem.Collections.GenericAssemblymscorlib.dll

 

概述

       它实现了一个可以动态添加、删除元素的强类型表结构,内部维护一个强类型T的数组 _items 来存放所有的元素,所以它只提供通过元素的位置来存取元素的方式。

它所提供的功能和内部实现与ArrayList 一样,唯一的区别是:ArrayList 相对来说是弱类型的,没有类型安全保障;而List<T> 相对来说是强类型的,有类型安全保障。

ArrayList:它操作的是一个 object 数组,所以加入它的数据都必须先转换为object 类型,这对于值类型来说就需要进行装箱操作,这也意味着ArrayList中可能存放着各种类型的数据,而且这些数据不一定兼容。

List<T>:它操作的是一个指定类型T的数组,这不仅避免了值类型的装箱操作,也提供了类型安全,所有加入它的数据都必须与T类型兼容。

 

功能说明

参照 ArrayList 的功能说明。

 

性能说明

       参照ArrayList 的性能说明。

 

适用场景

List<T>  ArrayList 的适用场景也只有一个区别——能不能保证各个元素数据类型的兼容性:能够保证就使用List<T>,不能保证就使用ArrayList

 

 

表的链表实现

LinkedList<T>NamespaceSystem.Collections.GenericAssemblySystem.dll

 

概述

       LinkedList<T> 提供了一个以双向链表方式实现的表结构,内部维护了一组 LinkedListNode<T> 类型的节点,每个节点都保存了自己所属的 LinkedList<T>list)、前一个节点(prev)、后一个节点(next)和储存在该节点的元素(item)。

       在链表形式的表中存在节点和元素两个概念,它们虽有关联但不尽相同。元素是表结构需要存储的真正数据,但是它不包含任何能够访问其它元素的信息,这样不可能实现链表形式的表;而节点不仅存储了表结构需要储存的真正数据,还储存了访问其它节点的信息。由上可以看出:元素是表结构需要存储的真正数据;而节点是这些元素的载体,是组成链表的基本单位。

       由于链表的组成单位是节点,所以对表元素的存取都必须通过储存元素的节点间接存取,不能直接存取。又因为每个节点都记录了前一个节点和后一个节点的内存地址,所以这些节点不需要存放在连续的内存空间中。

 

功能说明

       它提供了表的创建、清除、复制,表元素的存取、新增、删除和搜索。

表的创建:表的实例化,并可以根据传入的元素创建相应的节点,加入新建的表中。

表的清除:清除表中所有的节点。

表的复制:将表中所有节点中储存的元素复制(浅度复制)到指定数组的指定位置。

元素的存取:必须要先通过元素的搜索获取储存该元素的节点,然后再通过该节点对元素进行存取。

                   但是对于链表的第一个元素或最后一个元素的存取不用那么繁琐,只需要通过First  Last 这两个属性直接获取储存它们的第一个节点或最后一个节点。

元素的新增:新增一个元素其实就是新增一个节点,所以新增元素时可以传入要新增的元素,也可以传入包含该新增元素的节点,但是同一个节点不能加入到不同的链表中。

                   可以将元素添加到表头(AddFirst),也可以将元素添加到表尾(AddLast),还可以将元素插入到指定元素之前(AddBefore)或之后(AddAfter)。

元素的删除:删除一个元素其实就是删除一个节点,所以删除元素时可以传入要删除的元素,也可以传入要删除的节点(这个节点必须属于该表)。当传入的是要删除的元素时,会先根据该元素搜索到储存该元素的节点,然后将该节点删除。

                   除了可以删除指定的元素外,还可以删除特殊的元素:第一个元素(RemoveFirst)或最后一个元素(RemoveLast)。

元素的搜索:根据指定的元素从表中搜索储存该元素的节点。可以正向搜索(Find),也可以逆向搜索(FindLast)。

 

性能说明

元素的搜索:链表中的节点只能逐一的顺序访问,所以该操作的时间复杂度为 O(N)

元素的存取:链表的组成结构决定了它不能支持随机存取(即:不能通过元素位置来对元素进行直接存取),所以 LinkedList<T> 并不提供随机存取的方式。

                   因为可以通过 First  Last 属性直接获取链表的第一个节点或最后一个节点,所以链表中第一个元素或最后一个元素的存取操作的时间复杂度为 O(1)

                   其它的元素都必须先搜索到储存该元素的节点,然后再通过搜索到的节点对元素进行存取,所以该操作的时间复杂度为O(N)

元素的新增:链表中新增元素的操作比较简单,只需要修改前一节点的next、后一节点的prev 和储存该元素的节点的 prevnext的值。该操作的时间复杂度为 O(1)

元素的删除:链表中删除元素的操作比较简单,只需要修改前一个节点的next、后一个节点的prev并将删除节点的nextprevlist都重置。该操作的时间复杂度为 O(1)

 

适用场景

       当我们要操作的数据满足下面条件时就比较适合使用LinkedList<T>

1、  会频繁的进行元素的新增和删除;

2、  通常只会顺序的访问表中的所有元素,很少需要对元素进行随机存取;

3、  不需要对表中的元素进行排序。

posted @ 2011-09-27 10:34  Web游离者  阅读(3257)  评论(3编辑  收藏  举报