c# 集合 <转载>

C#集体类型( Collections in C#)

  集合是.NET FCL(Framework Class Library)中很重要的一部分,也是我们开发当中最常用到的功能之一,几乎是无处不在。俗话说知其然,知其所以然,平常看到IEnumerable,IEnumerator,ICollection是不是知道他们之间各自的区别?除了List和Dictionary以外,你还用过哪些其它的集合类?废话少说,今天我们就来看一些这些定义集合类的接口以及他们的实现。

集合接口

  先来看一下,FCL为我们提供了哪些接口:

  

IEnumerable 和IEnumberator


1

2

3

4

5

6

7

public interface IEnumerator

{

    bool MoveNext();

    object Current {  get; }

    void Reset();

}

  IEnumerator定义了我们遍历集合的基本方法,以便我们可以实现单向向前的访问集合中的每一个元素。而IEnumerable只有一个方法GetEnumerator即得到遍历器。


1

2

3

4

public interface IEnumerable

{

    IEnumerator GetEnumerator();

}

  注意:我们经常用的foreach即是一种语法糖,实际上还是调用Enumerator里面的Current和MoveNext实现的遍历功能。


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

List<string> list = new List<string>()

{

    "Jesse",

    "Chloe",

    "Lei",

    "Jim",

    "XiaoJun"

};

// Iterate the list by using foreach

foreach (var buddy in list)

{

    Console.WriteLine(buddy);

}

// Iterate the list by using enumerator

List<string>.Enumerator enumerator = list.GetEnumerator();

while (enumerator.MoveNext())

{

    Console.WriteLine(enumerator.Current);

}

  上面的代码中用到的foreach和enumerator到IL中最后都会被翻译成enumerator的MoveNext和Current。

  IEnumerable是一个很有用的接口,实现它的好处包括:

  1. 支持foreach语句
  2. 作为一个标准的集合类与其它类库进行交互
  3. 满足更复杂的集合接口的需求
  4. 支持集合初始化器

  当然实现的方法也有很多,如下:

  1. 如果我们集合是通过封装其它集合类而来的,那么我们可以直接返回这个集合的enumerator
  2. 通过yield return 来返回
  3. 实现我们自己的IEnumerator来实现

  这里给大家演示一下如何通过yield来实现返回enumerator


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

public class BuddyList : IEnumerable

{

    private string[] data= new string[]

    {

        "Jesse",

        "Chloe",

        "Lei",

        "Jim",

        "XiaoJun"

    };

    public IEnumerator GetEnumerator()

    {

        foreach (var str in data)

        {

            yield return str;

        }

    }

}

var myBuddies= new BuddyList();

foreach (var str in myBuddies)

{

    Console.WriteLine(str);

}

  

ICollection<T>和ICollection

  从最上面第一张图我们可以知道,ICollection是直接继承自IEnumerable。而实际上也是如此,我们可以说ICollection比IEnumerable多支持一些功能,不仅仅只提供基本的遍历功能,还包括:

  1. 统计集合和元素个数
  2. 获取元素的下标
  3. 判断是否存在
  4. 添加元素到未尾
  5. 移除元素等等。。。

  ICollection 与ICollection<T> 略有不同,ICollection不提供编辑集合的功能,即Add和Remove。包括检查元素是否存在Contains也不支持。

IList<T>和IList

  IList则是直接继承自ICollection和IEnumerable。所以它包括两者的功能,并且支持根据下标访问和添加元素。IndexOf, Insert, RemoveAt等等。我们可以这样说,IEnumerable支持的功能最少,只有遍历。而ICollection支持的功能稍微多一点,不仅有遍历还有维护这个集合的功能。而IList是最全的版本。

IReadOnlyList<T>

  这个是在Framework4.5中新增的接口类型,可以被看作是IList<T>的缩减版,去掉了所有可能更改这个集合的功能。比如:Add, RemoveAt等等。

IDictionary<TKey,TValue>

  IDictionary提供了对键值对集合的访问,也是继承了ICollection<T>和IEnumerable,扩展了通过Key来访问和操作数据的方法。

关联性泛型集合类

关联性集合类即我们常说的键值对集合,允许我们通过Key来访问和维护集合。我们先来看一下 FCL为我们提供了哪些泛型的关联性集合类:

  1. Dictionary<TKey,TValue>
  2. SortedDictionary<TKey,TValue>
  3. SortedList<TKey,TValue>

Dictionary<TKey,TValue>

  Dictionary<TKey,TValue>可能是我们最常用的关联性集合了,它的访问,添加,删除数据所花费的时间是所有集合类里面最快的,因为它内部用了Hashtable作为存储结构,所以不管存储了多少键值对,查询/添加/删除所花费的时间都是一样的,它的时间复杂度是O(1)。

  Dictionary<TKey,TValue>优势是查找插入速度快,那么什么是它的劣势呢?因为采用Hashtable作为存储结构,就意味着里面的数据是无序排列的,所以想按一定的顺序去遍历Dictionary<TKey,TValue>里面的数据是要费一点工夫的。

  作为TKey的类型必须实现GetHashCode()Equals() 或者提供一个IEqualityComparer否则操作可能会出现问题。

SortedDictioanry<TKey,TValue>

  SortedDictionary<TKey,TValue>和Dictionary<TKey,TValue>大致上是类似的,但是在实现方式上有一点点区别。SortedDictionary<TKey,TValue>用二叉树作为存储结构的。并且按key的顺序排列。那么这样的话SortedDictionary<TKey,TValue>的TKey就必须要实现IComparable<TKey>如果想要快速查询的同时又能很好的支持排序的话,那就使用SortedDictionary吧。

SortedList<TKey,TValue>

  SortedList<TKey,TValue>是另一个支持排序的关联性集合。但是不同的地方在于,SortedList实际是将数据存存储在数组中的。也就是说添加和移除操作都是线性的,时间复杂度是O(n),因为操作其中的元素可能导致所有的数据移动。但是因为在查找的时候利用了二分搜索,所以查找的性能会好一些,时间复杂度是O(log n)。所以推荐使用场景是这样地:如果你想要快速查找,又想集合按照key的顺序排列,最后这个集合的操作(添加和移除)比较少的话,就是SortedList了。

非关联性泛型集合类

  非关联性集合就是不用key操作的一些集合类,通常我们可以用元素本身或者下标来操作。FCL主要为我们提供了以下几种非关联性的泛型集合类。

  1. List<T>
  2. LinkedList<T>
  3. HashSet<T>
  4. SortedSet<T>
  5. Stack<T>
  6. Queue<T>

List<T>

  泛型的List 类提供了不限制长度的集合类型,List在内部维护了一定长度的数组(默认初始长度是4),当我们插入元素的长度超过4或者初始长度 的时候,会去重新创建一个新的数组,这个新数组的长度是初始长度的2倍(不永远是2倍,当发现不断的要扩充的时候,倍数会变大),然后把原来的数组拷贝过来。所以如果知道我们将要用这个集合装多少个元素的话,可以在创建的时候指定初始值,这样就避免了重复的创建新数组和拷贝值。

  另外的话由于内部实质是一个数组,所以在List的未必添加数据是比较快的,但是如果在数据的头或者中间添加删除数据相对来说更低效一些因为会影响其它数据的重新排列。

LinkedList<T>

  LinkedList在内部维护了一个双向的链表,也就是说我们在LinkedList的任何位置添加或者删除数据其性能都是很快的。因为它不会导致其它元素的移动。一般情况下List已经够我们使用了,但是如果对这个集合在中间的添加删除操作非常频繁的话,就建议使用LinkedList。

HashSet<T>

  HashSet是一个无序的能够保持唯一性的集合。我们也可以把HashSet看作是Dictionary<TKey,TValue>,只不过TKey和TValue都指向同一个对象。HashSet非常适合在我们需要保持集合内元素唯一性但又不需要按顺序排列的时候。

  HashSet不支持下标访问。

SortedSet<T>

  SortedSet和HashSet,就像SortedDictionary和Dictionary一样,还记得这两个的区别么?SortedSet内部也是一个二叉树,用来支持按顺序的排列元素。

Stack<T>

  后进先出的队列
  不支持按下标访问

Queu<T>

  先进先出的队列
  不支持按下标访问

推荐使用场景


集合


顺序排列


连顺存储


直接访问方式


访问时间


操作时间


备注


Dictionary



Key


Key:

O(1)


O(1)


访问性能最快,不支持排序


SortedDinctionary


顺序排列



Key


Key: 
O(log n)


O(log n)


快速访问和支持排序的折衷


SortedList


顺序排列



Key


Key:

O(log n)


O(n)


和SortedDictionary相似,只是内部用数据替代树作为存储结构。


List


使用者可以精确控制元素的位置



Index


Index: O(1)

Value: O(n)


O(n)


最适合需要直接访问每一个元素的少量集合。


LinkedList


使用者可以精确控制元素的位置



不支持


Value:

O(n)


O(1)


最适合不需要直接访问单个元素,但是在集合中添加/移除非常频繁的场景。


HashSet


不支持



Key


Key:

O(1)


O(1)


能保持元素唯一性的集合。不支持排序


SortedSet


顺序排列



Key


Key:

O(log n)


O(log n)


能保持元素唯一性并且支持排序。


Stack


LIFO



只能获取顶部元素


Top: O(1)


O(1)


Queue


FIFO



只能获底部元素


Front: O(1)


O(1)

非泛型类集合

泛型集合类是在.NET2.0的时候出来的,也就是说在1.0的时候是没有这么方便的东西的。现在基本上我们已经不使用这些集合类了,除非在做一些和老代码保持兼容的工作的时候。来看看1.0时代的.NET程序员们都有哪些集合类可以用。

  1. ArraryList

后来被List<T>替代。

  1. HashTable 后来被Dictionary<TKey,TValue>替代。
  2. Queue 后来被Queue<T>替代。
  3. SortedList 后来被SortedList<T>替代。
  4. Stack 后来被Stack<T>替代。

线程安全的集合类

  1. ConcurrentQueue 线程安全版本的Queue
  2. ConcurrentStack线程安全版本的Stack
  3. ConcurrentBag线程安全的对象集合
  4. ConcurrentDictionary线程安全的Dictionary
  5. BlockingCollection

.NET为我们提供的集合类是我们很常用的工具类之一,希望这篇文章能够帮助大家更好的认识这些集合类。当然,个人感觉还有不完善的地方,比如说HashTable和Binary Search Tree就没有细究下去,包括单向链表和双向链表之间的对比本文也没有提及。感兴趣的朋友可以深入了解一下。

时间: 2024-12-24 05:39:09

c# 集合 <转载>的相关文章

数组、字符串、集合

数组与集合的转换.数组与字符串的转换 ========数组变集合 String[] arr = {"abc","cc","kkkk"}; //把数组变成list集合有什么好处? /* 可以使用集合的思想和方法来操作数组中的元素. 注意:将数组变成集合,不可以使用集合的增删方法. 因为数组的长度是固定. contains. get indexOf() subList(); 如果你增删.那么会产生UnsupportedOperationExcepti

C#语言中数组和集合

数组.集合→用于储存多个同类型的数据数组 定长→用于保存固定数量的数据 在功能上,数组能实现的所有功能,集合都能实现:反之,集合能实现的某些功能,数组难以实现 占用内存少 便利速度快集合 不定长→保存的数据数量,可以在程序的执行过程中,发生变化 占用内存多 便利速度慢课时六:数组和集合 数组.集合→用于储存多个同类型的数据 数组 定长→用于保存固定数量的数据 在功能上,数组能实现的所有功能,集合都能实现:反之,集合能实现的某些功能,数组难以实现 占用内存少 便利速度快 集合 不定长→保存的数据数

通过反射了解集合泛型的本质

通过反射了解集合泛型的本质 import java.lang.reflect.Method; import java.util.ArrayList; /** * 通过反射了解集合泛型的本质 * @author shm * */ public class MethodDemo02 { public static void main(String[] args) { ArrayList list = new ArrayList(); list.add("hello"); list.add(

Java 之集合

collection(单列集合) List(有序,可重复) ArrayList     底层数据结构是数组,查询快,增删慢,线程不安全,效率高 Vector     底层数据结构是数组,查询快,增删慢,线程安全,效率低 LinkedList  底层数据结构是链表,查询慢,增删快,线程不安全,效率高 Set(无序,唯一) HashSet   底层数据结构是哈希表,依赖hashCode()和equals() 执行顺序:判断hashCode()是否相同,相同继续执行equals(),返回true不添加

集合和三元运算符

一.三元运算符 如下判断可以使用三元运算更方便的实现: a = 1b = 2 if a > 1: c = aelse: c = b 和下面是一样的: a = 1b = 2#下面这个c = a if a>1 else b 如下两种方法也是一样的:方式一: s=[]nums = list(range(1,11))for i in nums: if i%2==0: s.append(i)print(s)方式二:ss = [i for i in nums if i%2==0]print(ss)#这两种

集合函数 day4

1.集合 通过set强制转换,或者直接定义 1 nums = [0, 2, 4, 6, 8]#两种格式,带大括号与带中括号的 2 print(type(nums)) 3 aa = {0, 8, 2, 4, 6}#需要注意与字典的区别,字典是有分号的 2.交集

js算法集合(一) 水仙花数 及拓展(自幂数的判断)

js算法集合(一) ★ 最近有些朋友跟我说对js中的一些算法感到很迷惑,知道这个算法到底是怎么回事,但是就是不会用代码把它写出来,这里我跟大家分享一下做水仙花数的算法的思路,并对其扩展到自幂数的算法,希望能对大家有所帮助. 1.验证一个数是否为水仙花数 ①要写水仙花数的算法,我们首先来了解一下什么是水仙花数,水仙花数是指一个 3位正整数 ,它的每个位上的数字的 3次幂之和等于它本身.(例如:1^3 + 5^3+ 3^3 = 153): ②了解了什么是水仙花数我们就开始分析该怎么下手.通过定义来看

笔记:多线程-集合

阻塞队列(BlockingQueue) 当试图向队列添加元素而队列已满,或是想从队列移除元素而队列为空的时候,阻塞队列导致线程阻塞,阻塞队列接口定义如下,他继承Queue<E>接口: public interface BlockingQueue<E> extends Queue<E> { ????/** ???? * 添加一个元素,如果队列满,则抛出 IllegalStateException异常 ???? */ ????????boolean add(E e); ??

SQL入门之集合操作

尽管可以在与数据库交互时一次只处理一行数据,但实际上关系数据库通常处理的都是数据的集合.在数学上常用的集合操作为:并(union),交(intersect),差(except).对于集合运算必须满足下面两个要求: 两个数据集合必须具有同样数目的列 连个数据集中对应列的数据类型必须是一样的(或者服务器能够将其中一种类型转换为另一种类型) SQL语言中每个集合操作符包含两种修饰:一个包含重复项,另一个去除了重复项(但不一定去除了所有重复项). 0.union操作符 union和union all操作

Java—集合框架List

集合的概念 现实生活中:很多的事物凑在一起 数学中的集合:具有共同属性的事物的总和 Java中的集合类:是一种工具类,就像是容器,存储任意数量的具有共同属性的对象 集合的作用 在类的内部,对数据进行组织(针对作用与意义一样的属性,将他们放到一个集合中) 简单而快速的搜索大数量的条目 有的集合接口,提供了一系列排列有序的元素,并且可以在序列中快速的插入或删除有关元素 有的集合接口,提供了映射关系,可以通过关键字(key)去快速查找到对应的唯一对象,而这个关键字可以是任意类型 与数组相比 数组的长度