当前位置: 首页 > news >正文

C# 集合

转自 http://www.cnblogs.com/Youhei/archive/2010/06/19/1761010.html

目录

  • 集合及其实现的接口一览
  • 数组,索引器
  • 列表
  • 队列
  • 链表
  • 有序表
  • 字典
  • LookUp
  • HashSet
  • 位数组

本文中将如目录所示,总结较为常用的集合。

  • 集合及其实现的接口一览

下面罗列出来较为常用的集合类和由这些类实现的集合接口:

集合类

集合接口

ArrayList

IList, ICollection,IEnumerable

Queue

ICollection,IEnumerable

Stack

ICollection,IEnumerable

BitArray

ICollection,IEnumerable

Hashtable

IDictionary,ICollection,IEnumerable

SortedList

IDictionary,ICollection,IEnumerable

List<T>

IList<T>,ICollection<T>,IEnumerable<T>,IList,ICollection,IEnumerable

Queue<T>

IEnumerable<T>,ICollection,IEnumerable

Stack<T>

IEnumerable<T>,ICollection,IEnumerable

LinkedList<T>

IEnumerable<T>,ICollection<T>,ICollection,IEnumerable

HashSet<T>

IEnumerable<T>,ICollection<T>,IEnumerable

Dictionary<TKey, TValue>

IDictionary<TKey, TValue>,

ICollection<KeyValuePair<TKey, TValue>>,

IEnumerable<KeyValuePair<TKey, TValue>>,

IDictionary,ICollection,IEnumerable

SortedDictionary<TKey, TValue>

IDictionary<TKey, TValue>,

ICollection<KeyValuePair<TKey, TValue>>,

IEnumerable<KeyValuePair<TKey, TValue>>,

IDictionary,ICollection,IEnumerable

SortedList<TKey, TValue>

IDictionary<TKey, TValue>,

ICollection<KeyValuePair<TKey, TValue>>,

IEnumerable<KeyValuePair<TKey, TValue>>,

IDictionary,ICollection,IEnumerable

Lookup<TKey, TElement>

LKoopup<TKey, TElement>,

IEnumerable<IGrouping<TKey, TElemet>>,IEnumerable

  • 数组,索引器

数组对于大家都已经很熟悉了,这里就不再描述其概念了,我需要指出的是数组在拷贝过程中的浅复制和深赋值的区别和应该注意的事项。首先还是来看看下面的代码示例:

复制代码
    public class Element
    {
        public Element(int i) 
        {
            this.index = i;
        }
        public int index; } class Program { static void Main() { //值类型数组 int[] intArrA = new int[] { 1, 2 }; int[] intArrB = new int[intArrA.Length]; int index = 0; foreach (int i in intArrA) { intArrB[index++] = i; } //Array.Copy(intArrA, intArrB, intArrA.Length) //引用类型数组 Element[] elemArrA = new Element[] { new Element(11), new Element(22) }; Element[] elemArrB = new Element[elemArrA.Length]; index = 0; foreach (Element e in elemArrA) { elemArrB[index++] = e; } //Array.Copy(elemArrA, elemArrB, elemArrA.Length); //… } }
复制代码

上面的代码中首先有两个int类型的值类型数组,通过foreach语句将数组A的值赋给数组B;然后是两个Element引用类型的数组,并执行同样的赋值处理。那么,这两种数据类型上的该种赋值方式后又存在哪些区别呢?

数组intArrA的值赋值到了intArrB数组中,两数组元素的值的内存空间是相互独立,彼此不相干扰。但eleArrA和eleArrB两数组中存放的数据就有些微妙了——eleArrA和eleArrB中存放的都是指向数组eleArrA数组元素对象的内存地址的引用,我们来检验下,在上面的代码中“//… ”地方添加下面的代码:

复制代码
            intArrA[1] = 3;
            Console.WriteLine(" intArrA[1].index = " + intArrA[1]);
            Console.WriteLine(" intArrB[1].index = " + intArrB[1]);

            elemArrA[1].index = 33;
            Console.WriteLine(" elemArrA[1].index = " + elemArrA[1].index);
            Console.WriteLine(" elemArrB[1].index = " + elemArrB[1].index);
复制代码

运行后看下结果:

01

 

从上面的结果可以看出,当改变了值类型数组intArrA的第2个元素值为3后,intArrB数组的第2个元素的值并不会改变;不一样的是引用类型的数组中,当改变eleArrA的第2个元素的值为33后,eleArrA和eleArrB两数组的第2个元素的值都是33了,这就说明了eleArrA和eleArrB数组元素指向的是相同的对象内存。下面的示意图就很好的表明了数组的值复制和引用复制的区别:

02

接下来,再回到刚开始得示例代码中,我们可以看到这样两句语句被屏蔽了:

 //Array.Copy(intArrA, intArrB, intArrA.Length);
 //Array.Copy(elemArrA, elemArrB, elemArrA.Length);

 

其实,这两句语句和foreach语句的处理效果是一样的,都是将数组A的元素复制到数组B中,那么现在,我们就知道了浅复制是怎么回事了。因此,在处理引用数组时还是要注意浅复制并不是将数组的对象复制过来了,而只复制了指向对象的引用。

当然,相对于浅复制的就是深复制了,其意思就是复制过去的并不是对象的引用,而是新创建了和源对象拥有相同值新对象。将上面的代码稍作改动就可以了:

            foreach (Element e in elemArrA)
            {
                elemArrB[index] = new Element(index);
                elemArrB[index++].index = e.index;
            }

然后运行结果就会和前面的不一样了:

03

接下来,让我们看看索引器。

大家经常这样的访问数组的元素:intArrA[index]——通过下标来访问数组的元素。有些时候,我们的集合类中也想对过集合对象的下标操纵去访问集合元素,但C#中并不像C++中那样能重载"“[”和“]”运算符,于是乎,C#中提供了索引器了,也达到了同样的效果。

索引器是一种C#的语法构造,可以用我们熟悉的数组方括号语法来访问集合类中的元素。索引器是一种特殊的属性,有get()和set()访问方法指定其行为。索引器的声明如下:

                 类型 this[类型 参数]{get;set;}

其中“类型”决定了索引器返回的对象类型,而“类型 参数”则指定了可用何种参数索引包含目标对象的集合。虽然通常会用整数作为索引值,但实际中也可以用其他类型,包括字符串,甚至可以提供一个有多个参数的索引器来创建多维数组。this关键字是对索引器指向的对象的引用。作为一种正常的属性,必须定义get()和set()访问方法以确定所请求类型如何从集合中取出或赋值给集合。

复制代码
    public class MyList
    {
        public MyList(params String[] initStrArr) 
        {
            //初始化子项容量
            this.items = new String[256]; foreach (String s in initStrArr) { //注意,字符串虽然是引用类型的, //但C#编译器在内部已经处理过, //此处赋值的是深复制,而并非只复制字符串的引用 items[ctr++] = s; } } //添加子项到列表尾 public void Add(String the) { if (ctr >= items.Length) { throw new ArgumentOutOfRangeException(); } else { items[ctr++] = the; } } //索引器 public String this[int index] { get { if (index < 0 || index >= items.Length) { throw new ArgumentOutOfRangeException(); } return items[index]; } set { if (index >= ctr) { throw new ArgumentOutOfRangeException(); } else { items[index] = value; } } } public int GetItemNum() { return ctr; } private String[] items; private int ctr = 0; }
复制代码

上述代码创建了一个简单的集合类,可以使用索引器通过下标来访问集合元素了。

复制代码
        static void Main()
        {
            String[] resArr = new String[] { "zhangsan", "lisi", "wangwu", "zhaoliu"}; //创建列表对象,并用字符串数字resArr初始化列表 MyList list = new MyList(resArr); //向列表list中添加子项 list.Add("123456"); list.Add("987654"); list.Add("741852"); list.Add("369258"); //打印列表子项 for (int i = 0; i < list.GetItemNum(); i++) { Console.WriteLine("list[{0}] = {1}", i, list[i]); } }
复制代码

上面的代码比较简单,注释详细就不再说明了,运行看结果:

04

所以,索引器还是很好用的。   

  • 列表

 .Net 库为动态列表提供了类ArrayList和List<T>。System.Collections.Generic命名空间中的类List<T>的用法非常类似于System.Collection命名空间中的ArrayList类,本小节中主要探讨如何使用List<T>类。

<1>.创建列表

我们知道,泛型是类型安全的,所以假如我们有如下的声明:

            ArrayList objectList = new ArrayList();
            List<int> list = new List<int>();

那么,我们可以通过Add方法在objectList对象中添加任意类型的值,而在list中就只能添加int类型的值了:

05

从上图我们可以看到,试图给list添加Object类型的数据时,编译器会提示参数错误。

上面,我们通过调用默认的构造方法创建了列表集合对象,要知道,集合中有两个属性,其一是该集合的容量Capacity,其二是集合中拥有元素的个数Count。这两者并不对等的。集合的容量是表示该集合中能容纳的元素个数;而集合中拥有元素的个数是表示添加到该集合中的元素个数。Capacity的值始终大于或等于Count的值。前面用默认的构造函数创建一个集合对象的空列表,元素添加到列表中后,列表的容量就会扩大到可容纳4个元素,如果添加的元素个数大于列表对象目前容量时,列表会自动按照2倍的比例扩大列表的容量。比如添加5个元素时,列表容量就会从4扩大到8。

另外,我们还可以通过调用传参数的构造函数,来创建指定容量的列表对象,当列表中添加的元素个数达到或超过列表容量时,列表还是以2倍的关系扩大容量。如果列表中元素添加完毕后,需要将列表中空余的空间去掉,可以调用 list.TrimExcess()方法,注意:如果列表中的元素个数超过容量的90%,调用该方法将不会起任何的作用。

1.集合初始化器

C#3.0允许使用集合初始化器给集合赋值。集合初始化器类似于数组的初始化器。

            List<int> intList = new List<int>() { 1, 2, 3};
            List<String> strList = new List<string>() { "first string", "second string", "third string"}; List<Object> objList = new List<object>() { new Object(), new Object(), new Object()};

2.添加列表

我们可以通过Add()方法可以单个的添加元素,通过AddRange()方法可以批量的添加列表元素:

            intList.Add(12);
            intList.AddRange(new int[]{23, 34, 45, 56});
            objList.Add(new Object());
            objList.AddRange(new Object[]{new Object(), new Object()});

提示:集合初始化器只能在声明集合时使用,而AddRange()可以在初始化集合后调用。

3.插入列表

我们可以在指定的位置通过Insert()方法插入一个元素或通过InsertRange()插入大量的元素。如果插入的索引大于集合中的元素个数时,将会抛出ArgumentOutOfRangeException类型的异常。

            intList.Insert(3, 56);
            intList.InsertRange(3, new int[] { 0, 1, 3});

 

4.访问元素

执行了IList和IList<T>接口的所有类都提供了一个索引器,所以可以使用索引器,通过传递元素号来访问指定的元素,如果访问列表集合中所有元素,可以通过for或foreach遍历列表。

另外,List<T>类还提供了ForEach()方法,他用Action<T>参数声明。

public void ForEach(Action<T> action);

其中,Action<T>是一个泛型委托,其声明原型如下:

public delegate void Action<T> (T obj);

让我们来看看ForEach()方法的实现部分:

复制代码
        public class List<T> : IList<T> 
        {
            private T[] items;
            //...
            public void ForEach(Action<T> action) { if (action == null) throw new ArgumentNullException("action"); foreach(T item in items) { action(item); } } //... }
复制代码

因为List<T>实现了IEnumerable接口,所以可以使用foreach遍历列表,并用Action<T>委托处理列表数据。

5.删除元素

我们可以按照索引删除列表元素,例如:intList.Remove(3);语句将删除第4个元素。

另外,还可以直接将要删除的元素传递给Remove()方法,删除该元素,但按照索引删除比较快,因为按照对象删除需要先查找元素。

如果想要删除多个元素可以调用RemoveRange()方法。删除所有,可以调用clear()方法。删除指定特性的所有元素可以调用RemoveAll()方法。

6.搜索元素

转载于:https://www.cnblogs.com/viviancc/p/3875256.html

相关文章:

  • WPF中查找控件的扩展类
  • 获取IP和mac地址
  • OSPF ProcessID(进程号)详解
  • D3js技术文档 可视化展现
  • 推荐一本HTML5实例书,小白也看得会的《HTML5网页开发实例详解》
  • 使用apache bench(ab)压力测试
  • 编写高性能Web应用程序的10个技巧
  • java使用httpcomponents发送get请求
  • 比较大气的网页元素
  • oracle sql developer 无法启动的解决办法
  • RobotFrameWork(四)变量运算与Evaluate
  • vds与vcenter
  • wxPython Bind key events
  • 润乾报表实现预置分组报表及改进
  • mysql读写分离amoeba实现
  • C++11: atomic 头文件
  • css系列之关于字体的事
  • Fastjson的基本使用方法大全
  • JAVA多线程机制解析-volatilesynchronized
  • Map集合、散列表、红黑树介绍
  • nodejs:开发并发布一个nodejs包
  • Spring Security中异常上抛机制及对于转型处理的一些感悟
  • Vim Clutch | 面向脚踏板编程……
  • 读懂package.json -- 依赖管理
  • 分享几个不错的工具
  • 聊聊sentinel的DegradeSlot
  • 前端面试题总结
  • 深度解析利用ES6进行Promise封装总结
  • 深度学习在携程攻略社区的应用
  • 算法---两个栈实现一个队列
  • 学习ES6 变量的解构赋值
  • 学习JavaScript数据结构与算法 — 树
  • MPAndroidChart 教程:Y轴 YAxis
  • puppet连载22:define用法
  • #android不同版本废弃api,新api。
  • #define MODIFY_REG(REG, CLEARMASK, SETMASK)
  • #微信小程序:微信小程序常见的配置传值
  • #我与Java虚拟机的故事#连载11: JVM学习之路
  • $HTTP_POST_VARS['']和$_POST['']的区别
  • (1)(1.13) SiK无线电高级配置(五)
  • (ISPRS,2023)深度语义-视觉对齐用于zero-shot遥感图像场景分类
  • (LNMP) How To Install Linux, nginx, MySQL, PHP
  • (附源码)spring boot火车票售卖系统 毕业设计 211004
  • (七)Knockout 创建自定义绑定
  • (终章)[图像识别]13.OpenCV案例 自定义训练集分类器物体检测
  • (转)平衡树
  • .bat批处理(四):路径相关%cd%和%~dp0的区别
  • .NET Compact Framework 3.5 支持 WCF 的子集
  • .NET Standard 支持的 .NET Framework 和 .NET Core
  • .NET 药厂业务系统 CPU爆高分析
  • .NET 中 GetProcess 相关方法的性能
  • :如何用SQL脚本保存存储过程返回的结果集
  • @JsonSerialize注解的使用
  • [23] 4K4D: Real-Time 4D View Synthesis at 4K Resolution
  • [3D基础]理解计算机3D图形学中的坐标系变换