1为什么要用集合

  数组的局限性:数组元素个数固定,数组一旦定位就无法改变元素总数,如果有需求变化,则必须修改源码;    如果初始化元素总数非常大,则会造成空间浪费。

  集合的特点:根据需要动态增加个数,没有限制。

2泛型集合List<T>

  <T>表示泛型,T是Type的简写,表示当前不确定具体类型。

  可以根据用户的实际需要,确定当前集合需要存放的数据类型,一旦确定不可改变。

  使用泛型集合只能添加一种类型的数据,数据取出后无需强制转换。

  使用前引入命名空间:System.Collections.Generic

  常用方法:添加元素Add(<T>),删除元素Remove(索引)  元素个数:Count  使用可以用遍历foreach

  泛型集合的最大特性:严格约束集合内的元素类型

3泛型集合Dictionary<K,V>

  Dictionary<K,V>通常称为字典,<K,V>约束集合中元素类型。

  编译时检查类型约束,无需拆装箱操作,与哈希表操作类似。

4List<T>默认排序

  list.Sort();  基本数据类型可以直接排序,对于字符串默认按照拼音首字母升序排列

  调用集合中的Reverse()方法,实现元素反转。

  如果T不是基本数据类型而又想对其排序,T这个类必须继承IComparable<T>这个接口,并且必须对提供泛型接口要实现的方法,这个方法一般直接显示接口,但当此类继承自多个接口时需要显式实现接口。此实现接口方法的签名和返回值类型以及方法名不可更改。

1         public int CompareTo(Student other)
2         {
3             //return other.stuName.CompareTo(this.stuName);   //other在前,降序
4 
5             return this.StuId.CompareTo(other.StuId);  //this在前,升序
6         }    
IComparable接口

  默认的排序方式,只能有一种。

5List<T>动态排序

  如果仅对姓名进行升序或学号进行降序等可只用List<T>默认排序。List<T>只有一种排序方式。

  但在实际项目中,可能有时是姓名升序、有时是姓名降序、有时是学号升序、有时是学号降序,需要用到ICompare<T>接口。使用比较器ICompare<T>实现动态排序。

  默认排序可完全被比较器IComPare<T>所替代。

 1     //年龄升序排列
 2     class AgeASC : IComparer<Student>
 3     {
 4         public int Compare(Student x, Student y)
 5         {
 6             return x.Age - y.Age;
 7         }
 8     }
 9     //年龄降序排列
10     class AgeDESC : IComparer<Student>
11     {
12         public int Compare(Student x, Student y)
13         {
14             //return y.Age - x.Age;
15             return y.Age.CompareTo(x.Age);
16         }
17     }
18     //姓名升序排列
19     class NameASC : IComparer<Student>
20     {
21         public int Compare(Student x, Student y)
22         {
23             return x.StuName.CompareTo(y.StuName); //x在前,升序
24         }
25     }
26     //姓名降序排列
27     class NameDESC : IComparer<Student>
28     {
29         public int Compare(Student x, Student y)
30         {
31             return y.StuName.CompareTo(x.StuName); //y在前,降序
32         }
33     }
IComparer比较器接口

6总结Sort()方法

6.1List集合的Sort方法共有4种。

  Sort():使用默认比较器IComparable<T>排序对象。

  Sort(ICompare<T> compare):将实现比较器接口的对象作为参数,这个对象是指继承了此比较器接口的类的对象。

6.2集合排序总结

  如果是基本数据类型元素,可以直接排序。

  如果是对象类型元素:

    1.当排序只有一种的时候,可以使用默认比较器IComparable<T>在类中直接实现接口即可。

    2.当需要多种排序的时候,需要添加对应排序类,并给每一个排序类实现比较器接口ICompare<T>来完成不同排序方法。

7使用Linq排序

 1 static void Main(string[] args)
 2         {
 3             //实例化List<T>集合对象
 4             List<Student> list = new List<Student>();
 5             //添加对象元素
 6             Student objStu1 = new Student() { Age = 20, StuId = 1001, StuName = "小张" };
 7             Student objStu2 = new Student() { Age = 25, StuId = 1003, StuName = "小李" };
 8             Student objStu3 = new Student() { Age = 22, StuId = 1002, StuName = "小王" };
 9             list.Add(objStu1);
10             list.Add(objStu2);
11             list.Add(objStu3);
12             //默认排序
13             Console.WriteLine("------------默认排序------------");
14             for (int i = 0; i < list.Count; i++)
15             {
16                 Console.WriteLine("姓名:{0}  年龄:{1}  学号:{2}", list[i].StuName, list[i].Age, list[i].StuId);
17             }
18             //年龄降序排序
19             Console.WriteLine("------------年龄降序排序------------");
20             List<Student> stuList = list.OrderByDescending(s => s.Age).ToList();
21             for (int i = 0; i < stuList.Count; i++)
22             {
23                 Console.WriteLine("姓名:{0}  年龄:{1}  学号:{2}", stuList[i].StuName, stuList[i].Age, stuList[i].StuId);
24             }
25             //姓名升序排序
26             Console.WriteLine("------------姓名升序排序------------");
27             stuList = list.OrderBy(s => s.StuName).ToList();
28             for (int i = 0; i < list.Count; i++)
29             {
30                 Console.WriteLine("姓名:{0}  年龄:{1}  学号:{2}", stuList[i].StuName, stuList[i].Age, stuList[i].StuId);
31             }
32             Console.ReadLine();
33         }
34         #endregion
使用LINQ排序

8.泛型

  8.1泛型:没有写死参数类型,调用的时候才指定的类型;这属于延迟声明,把参数类型的声明推迟到调用,推迟一切可以推迟的,延迟思想   

List<T>  不是语法糖,是2.0由框架升级提供的功能,需要编译器支持(由代码到exe或dll,由VS编译器完成)占位符+JIT即时编译(双击exe时发生,由clr实现)  generic≈common>object 

可以使用泛型方法、泛型类、泛型接口、泛型委托。泛型类可以被继承,但继承的时候必须指定参数类型;可以在子类中指明参数类型,也可以只在泛型类指明参数类型;泛型接口 继承和泛型类继承是一样的。

  8.2泛型约束:可以是基类约束、接口约束、引用类型约束(T:class)、值类型约束(T:struct)、无参数构造函数约束(T:new())       约束多个条件是相或的逻辑,但多个约束的话必须互斥

  where T : class      //引用类型约束,可以赋值null

  where T : struct     //值类型约束,可以赋值默认值Default(T)

  where T :new()     //无参数构造函数   T tNew = new T();

  默认值——>>值类型示例  T t = default(T) ;  //前提是值类型,这个default会根据T的不同,赋予默认值

  默认值——>>无参数构造函数约束示例  T t = new T ();

  泛型使用的方法是参数类型未事先指明,任何类型都可以进来,这样做是不安全的。所以这时需要使用到泛型约束。约束可以是基类约束,例如Where T:People,就指明这里的T必须是People,不能Dog/Cat之类的。赋予了T:People,强制保证T一定是People或者People的子类,就可以使用基类的一切属性和方法。

  约束可以叠加,Where T : People,ISports   //表明既可以继承People类,又可以继承ISports接口

  约束的基类不能是密封类,约束可以带来权利,使用基类或接口的一切属性和方法;同时约束强制义务,保证T一定是基类或基类的子类

  8.3协变和逆变

    public class Bird
    {
        public int Id { get; set; }
    }

    public class Sparrow : Bird
    {
        public string Name { get; set; }
    }
声明两个类:Bird和Sparrow(麻雀),Sparrow继承于Bird
                Bird bird1 = new Bird();            //声明一只鸟,就是一只鸟,OK
                Bird bird2 = new Sparrow();         //声明一只鸟,指明它是麻雀,OK
                Sparrow sparrow1 = new Sparrow();   //声明一只麻雀,就是一只麻雀,OK
                //Sparrow sparrow2 = new Bird();    //声明一个麻雀,指明它是一只鸟,不OK,因为并不是所有的鸟都是麻雀,也有可能是鹦鹉
单个变量的声明
                List<Bird> birdList1 = new List<Bird>();            //OK,声明一群鸟,指明它们就是一群鸟
                //List<Bird> birdList2 = new List<Sparrow>();       //不OK
                //声明一群鸟,指明它们就是一群麻雀,道理上行得通,因为一堆麻雀当然是一堆鸟。但C#两个集合对象之间没有父子关系
                //Bird bird2 = new Sparrow();是OK的,因为Bird与Sparrow之间存在父子关系
                List<Bird> birdList3 = new List<Sparrow>().Select(c => (Bird)c).ToList();
List<>变量的声明·
                IEnumerable<Bird> birdList1 = new List<Bird>();
                IEnumerable<Bird> birdList2 = new List<Sparrow>();

                Func<Bird> func = new Func<Sparrow>(() => null);
协变
                ICustomerListIn<Bird> birdList1 = new CustomerListIn<Bird>();
                birdList1.Show(new Sparrow());
                birdList1.Show(new Bird());

                Action<Sparrow> act = new Action<Bird>((Bird i) => { });
逆变

   8.4泛型缓存

  泛型缓存读取效率比字典缓存高很多,不能主动释放,静态的东西永远在内存中

 

9.数据结构

  9.1 【数组】  如int[]是引用类型,元素的内存连续摆放,支持索引,读取快,插入增加删除慢

Array int[]  string[] 元素类型必须一致 定长 无装箱和拆箱
ArrayList ArrayList arrayList = new ArrayList(); 元素类型可以不一致 不定长 有装箱和拆箱(objcet)
List List<string>  List<Student> 元素类型必须一致 不定长 无装箱和拆箱(支持泛型,保证类型安全)

  9.2 【链表】 支持泛型,不连续分配,不支持索引,读取慢增删快

LinkedList 双向链表 找元素只能遍历,查找不方便 AddLast   AddFirst  Find
Queue 队列 先进先出,不是线程安全 Enqueue:插入  Dequeue:取走最先放进去的元素  Peek:获取不移除
Stack 先进后出,堆栈:最早实例化的内容一定是最后释放的 Push:插入  Pop:获取并移除  Peek:获取不移除

  9.3 【集合】  泛型,前后元素没节点记录,元素内存也不连续无法使用索引,可以去重+交差并补

HashSet 哈希集合 可以去重,交差并补,不常用

Add:添加

SymmetricExceptWith:补

UnionWith:并

ExceptWith:差

IntersectWith:交

SortSet 排序集合 可以排序,去重+排序,交差并补 IComparer<T>

  9.4 【Key-Value】   增删查改都很快,因为key和value是两个数组,所以是典型的空间换时间,当然数据量太大,读写速度可能会下降,添加方式有两种:Add(key,value)  [key]=value  第二种索引方式如果没有key是添加,有key是赋值修改  key是唯一的,重复添加会报错Key-Value类型还有HashMap,KeyValuePair,NameValueCollection

Hashtable 哈希table

object有装箱拆箱,拿着Key找Value,无序的

HashTable table =new Hashtable();

table.Add("123","456");

table[222]=888;

线程安全:只有一个线程写,多个线程读

Hashtable.Synchronized(table);

Dictionary 字典 支持泛型,有类型约束,有序的(遍历的顺序会取决于添加的顺序)  Dictionary和HashTable都是object类型下,HashTable比Dictionary性能要好

非线程安全,可能多个线程同时读写;线程安全的是ConcurrentDictionary

SortedDictionary 排序字典   Dictionary+排序,因为有排序,效率比Dictionary低 IComparer<>指定排序规则  
SortedList 排序列表 按照key进行排序 IComparer<>指定排序规则 TrimToSize()  用于最小化集合的内存开销

 ConcurrentQueue是线程安全版本的Queue;ConcurrentStack是线程安全版本的Stack;ConcurrentBag是线程安全的对象集合;ConcurrentDictionary是线程安全的Dictionary

  9.5 接口是标识功能的,只要继承了这个接口,就拥有这个接口的功能;不同的接口拆开,就是为了接口隔离,接口有可能重复

IList 继承IEnumerable,支持索引,[index],IndexOf(),Insert(),RemoveAt()
IEnumerable

任何数据集合都继承它,凡继承此接口,就可以用foreach()遍历,遍历才会去查询比较 迭代器 yield

为不同的数据结构,提供统一的数据访问接口,yield是按需获取,用yield实现了延迟获取,如果foreach中间有

break,比for节省了时间

ICollection 继承IEnumerable,可变长的,统计集合中对象的标准接口, Count,Add(),Clear(),Contains(),Remove,CopyTo()
IQueryable 表达式目录树的解析,延迟到遍历的时候才会去执行