zoukankan      html  css  js  c++  java
  • 第十八章、使用集合

      什么是集合类

      Microsoft .NET Framework提供了几个类,它们集合元素,并允许应用程序以特殊方式访问这些元素。这些类正是集合类,它们在System.Collections.Generic命名空间中。

      List集合类

      泛型List类是最简单的集合类。用法和数组差不多,可以使用标准数组语法(方括号和元素索引)来引用集合中的元素(但不能用这种语法在集合初始化之后添加新元素)。List类比数组灵活,避免了数组以下限制。

      1、为了改变数组大小,必须创建新数组,复制数组元素(如果新数组较小,甚至还复制不完)。然后更新对原始数组的引用,使其引用新数组。

      2、如果删除一个数组元素,之后的所有元素都必须上移一位。即使这样还不行,因为最后一个元素会产生两个拷贝。

      3、如果插入一个数组元素,必须使元素下移一位来腾出空位。但最后一个元素就丢失了!

      List集合类通过以下功能来避免这些限制:

      1、不需要在创建List集合时指定容量,它能随着元素的增加而自动伸缩。这种动态行为当然是有开销的,如有必要可以指定初始大小。超过这个大小,List集合会自动增大。

      2、可用Remove方法从List集合中删除指定元素。List集合自动重新排序并关闭裂口。还可以用RemoveAt方法删除List集合指定位置的项。

      3、可用Add方法在List集合尾部添加元素。只需提供添加的元素,List集合的大小会自动改变。

      4、可用Insert方法在List集合中部插入元素。同样地,List集合的大小会自动改变。

      5、可调用Sort方法轻松对List对象中的数据排序

      List numbers = new List();

      //使用Add方法填充List

      foreach(int number in new int[12]{10,9,8,7,7,6,5,10,4,3,2,1})

      {

      numbers.Add(number);  //10,9,8,7,7,6,5,10,4,3,2,1

      }

      //在列表倒数第二个位置插入一个元素

      //第一个参数是位置,第二个参数是要插入的值

        numbers.Insert(numbers.Count-1,99);  //10,9,8,7,7,6,5,10,4,3,2,99,1

      //删除值是7的第一个元素(第四个元素,索引4)

      numbers.Remove(7);//10,9,8,7,6,5,10,4,3,2,99,1

      //删除当前第7个元素,索引6(10)

      numbers.Remove(7);//10,9,8,7,6,5,4,3,2,99,1

      //用for语句遍历剩余11个元素

      for(int i=0;i

      {

      int number = numbers[i];

      Console.WriteLine(number);   //10,9,8,7,6,5,4,3,2,99,1

      }

      //用foreach语句遍历剩余11个元素

      foreach(int number in numbers)

      {

      Console.WriteLine(number);

      }

      LinkedList集合类

      LinkedList集合类实现了双向链表。列表中的每一项除了容纳数据项的值,还容纳了对下一项的引用(Next属性)以及对上一项的引用(Previous属性)。列表起始项的Previous属性设为null,最后一项的Next属性设为null。

      和List类不同,LinkedList不支持用数组语法插入和获取元素。相反,要用AddFirst方法在列表开头插入元素,下移原来的第一项并将它的Previous属性设为对新项的引用。或者用AddLast方法在列表尾插入元素,将原来最后一项的Next属性设为对新项的引用。还可使用AddBefore和AddAfter方法在指定项前后插入元素(要先获取项)。

      First属性返回对LinkedList集合第一项的引用,Last属性返回对最后一项的引用。为了遍历链表,可以从它的任何一端开始,查询Next或Previous引用,直到null为止。还可以使用foreach语句正向遍历LinkedList对象,抵达末尾自动停止。

      从LinkedList集合中删除项是使用Remove,RemoveFirst和RemoveLast方法。

      LinkedList numbers = new LinkedList();

      //使用AddFirst方法填充列表

      foreach(int number in new int[]{10,8,6,4,2})

      {

      numbers.AddFirst(number);  //2 , 4 , 6 , 8 ,`10

      }

      //用for语句遍历

      Console.WriteLine("Iterating using a for statement:");

      for(LinkedList node = numbers.First; node != null;node = node.Next)

      {

      int number = node.Value;

      Console.WriteLine(number);//2 , 4 , 6 , 8 ,`10

      }

      //用foreach语句遍历

      foreach(int number in numbers)

      {

      numbers.AddFirst(number);  //2 , 4 , 6 , 8 ,`10

      }

      //反向遍历(只能用for,foreach只能正向遍历

      for(LinkedList node = numbers.Last; node != null;node = node.Previous)

      {

      int number = node.Value;

      Console.WriteLine(number);//10,8,6,4,2

      }

      Queue集合类

      Queue类实现了先入先出队列元素在队尾插入(入队或Enqueue),从队头移除(出队或Dequeue)。

      Queue numbers = new Queue();

      //填充队列

      Console.WriteLine("Populating the queue");

      foreach(int number in new int[4]{9,3,7,2})

      {

      numbers.Enqueue(number);

      Console.WriteLine("{0} has joined the queue",number);

      }

      //遍历队列

      foreach(int number in numbers)

      {

      Console.WriteLine(number);

      }

      //清空队列

      while(numbers.Count>0)

      {

      int number = numbers.Dequeue();

      Console.WriteLine("{0} has left the queue",number);

      }

      Stack集合类

      Stack类实现了后入先出的栈元素在顶部入栈(push),从顶部出栈(pop)。通常可以将栈想象成一叠盘中:新盘子叠加到顶部,同样从顶部取走盘子。

      Stack numbers = new Stack();

      //填充栈—入栈

      Console.WriteLine("Pushing items onto the stack:");

      foreach(int number in new int[4]{9,3,7,2})

      {

      numbers.Push(number);

      Console.WriteLine("{0} has been pushued on the stack",number);

      }

      //遍历栈

      foreach(int number in numbers)

      {

      Console.WriteLine(number);//2,7,3,9

      }

      //清空栈

      while(numbers.Count>0)

      {

      int number = numbers.Pop();

      Console.WriteLine("{0} has been popped on the stack",number);}//2,7,3,9

      }

      Dictionary<tkey,tvalue>集合类

      数组和List类型提供了将整数索引映射到元素的方式。在方括号中指定整数索引(例如[4])来获取索引4的元素(实际是第五个元素)。但有时需要从非int类型(比如string,double或Time)映射。其他语言一般把这称为关联数组。C#的Dictionary<tkey,tvalue>类在内部维护两个数组来实现该功能。一个keys数组容纳要从其映射的键,另一个value容纳映射到的值。在Dictionary<tkey,tvalue>集合中插入键/值对时,将自动记录哪个键和哪个值关联,从而允许开发人员快速和简单地获取具有指定键的值。Dictionary<tkey,tvalue>类设计有一些重要的结果。

      1、Dictionary<tkey,tvalue>集合不能包含重复的键。调用Add方法添加数组中已有的键将会引发异常。但是,如果使用方括号记号法来添加键/值对,就不用担心异常——即使之前已添加了相同的键。如果键已经存在,其值会被新值覆盖。可用ContainKey方法测试Dictionary<tkey,tvalue>集合是否已包含特定的键。

      2、Dictionary<tkey,tvalue>集合内部采用一种稀疏数据结构,在有大量内存可用时才 最高效。随着更多元素的插入,Dictionary<tkey,tvalue>集合可能快速消耗大量内存。

      3、用foreach语句遍历Dictionary<tkey,tvalue>集合返回一个KeyValuePair<tkey,tvalue>。该结构包含数据项的键和值的拷贝,通过Key和Value属性访问每个元素。元素是只读的,不能用它们修改Dictionary<tkey,tvalue>集合中的数据。

      Dictionary<string,int> ages = new Dictionary<string,int>();

      //填充字典

      ages.Add("John",47); //使用Add方法

      ages.Add("Diana",46);

      ages["James"] = 20; //使用数组语法 可包含重复的键,如果键已存在,其值会被新值覆盖

      ages["Francesca"] = 18;

      //用foreach语句遍历字典

      //迭代器生成的是一个KeyValuePair项

      Console.WriteLine("The Dictionary contains:");

      foreach(KeyValuePair<string,int> element in ages)

      {

      string name = element .Key;

      int age = element .Value;

      Console.WriteLine("Name:{0},Age:{1}",name,age);

      }

      SortedList<tkey,tvalue>集合类

      SortedList<tkey,tvalue>类与Dictionary<tkey,tvalue>类非常相似,都允许将建和值关联。主要区别是,前者的keys数组总是排好序的。在SortedList<tkey,tvalue>对象中插入数据花的时间较长,但获取数据会快一些,而且SortedList<tkey,tvalue>类消耗的内存较少。

      在SortedList<tkey,tvalue>集合中插入一个键/值对时,键会插入keys数组的正确索引位置,目的是确保keys数组始终处于排好序的状态。然后,值会插入values数组的相同索引位置。SortedList<tkey,tvalue>类自动保证键值同步,即使是在添加和删除了元素之后。这意味着可按任意顺序将键/值对插入一个SortedList<tkey,tvalue>,它们总是根据键来排序。

      和Dictionary<tkey,tvalue>类相似,SortedList<tkey,tvalue>集合不能包含重复的键。用foreach语句遍历SortedList<tkey,tvalue>集合返回的是KeyValuePair<tkey,tvalue>对象,只是这些KeyValuePair<tkey,tvalue>对象会根据Key属性排好序。

      SortedList<string,int> ages = new SortedList<string,int> ();

      //填充有序列表

      ages.Add("John",47); //使用Add方法

      ages.Add("Diana",46);

      ages["James"] = 20; //使用数组语法

      ages["Francesca"] = 18;

      //用foreach语句遍历有序列表

      //迭代器生成的是一个KeyValuePair项

      Console.WriteLine("The SortedList contains:");

      foreach(KeyValuePair<string,int> element in ages)

      {

      string name = element .Key;

      int age = element .Value;

      Console.WriteLine("Name:{0},Age:{1}",name,age);

      }

      HashSet集合类

      HashSet类专为集合操作优化,操作包括设置成员和生成并集/交集等。

      数据项用Add方法插入HashSet集合,用Remove方法删除。但是,HashSet类真正强大的是它的IntersectWith,UnionWith,ExceptWith方法。这些方法修改HashSet集合来生成与另一个HashSet相交、合并或者不包含其数据项的新集合。这些操作是破坏性的,因为会用新集合覆盖原始HashSet对象的内容。另外,还可以使用IsSubsetOf,IsSupersetOf,IsProperSubsetOf,IsProperSupersetOf方法判断一个HashSet集合的数据是否另一个HashSet集合的超集或子集。这些方法返回Boolean值,是非破坏性的。

      HashSet employees = new HashSet(new string[]{"Fred","Bert","Harry","John"});

      HashSet customers = new HashSet(new string[]{"John","Sid","Harry","Diana"});

      Concole.WriteLine("Employees:");

      foreach(string name in employees)

      {

      Concole.WriteLine(name); //Fred Bert Harry John

      }

      Concole.WriteLine(" Customers:");

      foreach(string name in customers )

      {

      Concole.WriteLine(name); //John Sid Harry Diana

      }

      Concole.WriteLine(" Customers who are also employees:");//既是客户又是员工的人

      customers.IntersectWith(employees );//IntersectWith操作是破坏性的,新集合覆盖原来的customers

      foreach(string name in customers )

      {

      Concole.WriteLine(name); //John Harry

      }

      使用集合初始化器

      List numbers  = new List(){10,9,8,7,7,6,5,10,4,3,2,1};

      C#编译器内部会将初始化转换成一系列Add方法调用。换言之,只有支持Add方法的集合才能这样写。

      对于获取键/值对的复杂集合,可在集合初始化器中将每个键/值对指定为匿名类型,如下:

      Dictionary<string,int> ages = new Dictionary<string,int>(){{"John",47},{"Diana",46},{"James",21},{"Francesca",18}};

      Find方法、谓词和Lambda表达式

      面向字典的集合(Dictionary<tkey,tvalue>,SortedDictionary<tkey,tvalue>,SortedList<tkey,tvalue>)允许根据键来快速查找值,支持用数组语法访问值。对于List和LinkedList等支持无键随机访问的集合,它们无法通过数组语法来查找项,所以专门提供了Find方法。Find方法的实参是代表搜索条件的谓词。谓词就是一个方法,它检查集合的每一项,返回Boolean值指出该项是否匹配。Find方法返回的是发现的第一个匹配项。List和LinkedList类还支持其他方法,例如FindLast返回最后一个匹配项。List类还专门有一个FindAll方法,它返回所有匹配项的一个List集合。

      谓词最好用Lambda表达式指定。简单地理解,Lanmda表达式是能返回方法的表达式。

      方法通常是4部分组成:返回类型、方法名、参数列表和方法主体。但Lambda表达式只包含其中的两个元素:参数列表和方法主体。Lambda表达式没有定义方法名,返回类型(如果有的话)则根据Lambda表达式的使用上下文推断。

      struct Person

      {

      public int ID{get;set;}

      public string Name{get;set;}

      public int Age{get;set;}

      }

      //创建并填充personnel列表

      List personnel = new List<>(Person)

      {

      new Person(){ID =1, Name = "John", Age = 47},

      new Person(){ID =2, Name = "Sid", Age = 28},

      new Person(){ID =3, Name = "Fred", Age = 34},

      new Person(){ID =4, Name = "Paul", Age = 22},

      };

      //查找ID为3的第一个列表成员

      Person match = personnel.Find((Person p)=>{return p.ID == 3; });.

      Console.WriteLine("{0},{1},{2}",match.ID,match.Name,match.Age);

      调用Find方法时,实参(Person p)=>{return p.ID == 3; }就是实际“干活儿”的Lambda表达式,它包含以下语法元素。

      1、圆括号中的参数列表。和普通方法一样,即使Lambda表达式代表的方法不获取任何参数,也要提供一对空白圆括号。如:(Person p)

      2、=>操作符,它向C#编译器指出这是一个Lambda表达式。

      3、Lambda表达式主体(方法主体)如:{return p.ID == 3; }

  • 相关阅读:
    javascript判断浏览器类型与版本
    javascript动态创建VML
    getElementsByAttribute
    javascript替换字符
    javascript contains方法
    sql2005性能优化(在32位系统上突破2G内存使用量的方法)
    详解如何让WIN2003和SQL2005支持4G以上内存
    搭建Windows CE 6.0团队开发环境
    MS SQL Server启用对4G以上物理内存的支持
    USB 3G Wireless Modem(2010.10.28更新)
  • 原文地址:https://www.cnblogs.com/linhuide/p/5819899.html
Copyright © 2011-2022 走看看