博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

C#语言学习之旅(9)--集合

Posted on 2013-01-09 11:57  米粒3  阅读(186)  评论(0编辑  收藏  举报

当元素的个数是动态的,就应使用集合类,集合类一般有List<T>,ArrayList、数组、队列、堆栈、链表等。下面来介绍各种集合的使用方法。

9.1 列表

对象类型的集合主要位于System.collections命名空间中;泛型集合主要是在System.collections.Generic命名空间中,当然List <T>肯定也都在给命名空间下,下面来介绍列表的使用

1.列表初始化

List<int> intList = new List<int>() { 1, 2 };

定义一个int类型的列表。

2.添加一个元素

intList.Add(3);

 

3.添加多个元素

intList.AddRange(new int[] { 4, 5 });

AddRange方法的参数是一个IEnumerable<T>类型的对象,当然也可以传送一个数组,上面示例即传送数组。

4.访问元素

访问元素和访问数组很类似的,主要是list<T>都提供了一个索引器,也就是继承了ICollection接口,由于已经提供了索引器了,那么我们就可以像访问数组那样访问数据。

int i=intList[0];

5.遍历数据

一种是可以使用for来循环迭代集合中的每个元素,使用索引器来访问数据,另一种是使用ForEach主要是Ilist<T>同时也继承了接口IEnumerable,该接口主要是用来迭代数据的。

如下面代码:

 

Console.WriteLine("For Loop");
for (int i = 0; i < intList.Count;i++)
{
Console.WriteLine(intList[i]);
}
Console.WriteLine("ForEach Loop");
foreach(int i in intList)
{
Console.WriteLine(i.ToString());
}

9.2 队列

队列是其元素以先进先出的方式处理集合。先放进在队列的元素会先读取 。队列的例子很多的,我们常见的就是排队,排在前面的先服务,后来的后服务。

队列和列表的主要区别在于队列没有执行IList接口,所有不能用索引器访问队列。只能添加元素,下面给出个例子来:

队列demo
    //队列的使用
   public class DocumentManager
   {
      
private readonly Queue<Document> documentQueue = new Queue<Document>();//定义一个只读的队列

      
public void AddDocument(Document doc)
      {
         
lock (this)
         {
            documentQueue.Enqueue(doc);
//队列尾部添加元素
         }
      }

      
public Document GetDocument()
      {
         Document doc 
= null;
         
lock (this)
         {
            doc 
= documentQueue.Dequeue();//队列头部获得元素
         }
         
return doc;
      }

      
public bool IsDocumentAvailable
      {
         
get
         {
            
return documentQueue.Count > 0;//判断队列是否有元素
         }
      }
   }

9.3 栈

栈和队列是非常相似的集合,只是栈是后进先出而已。

栈Demo
     Stack<char> alphalist = new Stack<char>();
            alphalist.Push(
'A');
            alphalist.Push(
'B');

            
foreach(char a in alphalist)
            {
                Console.WriteLine(a);
            }

说明:由上面的例子可以得出,输入A,B,输出结果为B,A,那么可以得出栈是后进先出。

9.4 链表

LinkedList<T>是一个双向链表,其元素指向他的前后的元素。链表的优点是,能够快速插入列表中间的数据。缺点就是查找速度比较慢。

9.5 有序表

SortList<TKey,Tvalue>,这个类按照键值给元素排序

有序表demo
 SortedList<stringstring> sortList = new SortedList<stringstring>();
            sortList.Add(
"1""demo");
            sortList.Add(
"2""demo2");

            
foreach(KeyValuePair<string,string> m in sortList)
            {
                Console.WriteLine(
"{0},{1}", m.Key, m.Value);
            }

9.6 字典

字典表示一种复杂的数据结构。字典也成为隐射或散列表。字典的特性是能够快速查找值,也可以自由添加和删除元素。并且没有在内存中移动后续元素的性能开销。

用作字典中的键的类型必须重写object类得GetHashCode方法。只要字典类需要确定元素的位置,就要调用GetHashCode方法。该方法返回的Int由字典用于计算放置元素的索引。另外键类型还必须执行IEquality.Equals的方法或重写object的equals方法,主要是防止不同键值对象可能返回相同的散列码,所以字典使用Equals方法来比较键。下面给出个示例:

字典demo
  [Serializable]
   
public struct EmployeeId  : IEquatable<EmployeeId>
   {
      
private readonly char prefix;
      
private readonly int number;

      
public EmployeeId(string id)
      {
         
if (id == nullthrow new ArgumentNullException("id");

         prefix 
= (id.ToUpper())[0];
         
int numLength = id.Length - 1;
         number 
= int.Parse(id.Substring(1, numLength > 6 ? 6 : numLength));
      }

      
public override string ToString()
      {
         
return prefix.ToString() + string.Format("{0,6:000000}", number);
      }

        
      
public override int GetHashCode()
      {
         
return (number ^ number << 16* 0x15051505;
      }


      
public bool Equals(EmployeeId other)
      {
         
return (prefix == other.prefix && number == other.number);
      }

       
public override bool Equals(object obj)
       {
           
if (!(obj is EmployeeId)) return false;
           
return Equals((EmployeeId)obj);
       }

       
public static bool operator ==(EmployeeId emp1, EmployeeId emp2)
       {
           
return emp1.Equals(emp2);
       }

       
public static bool operator !=(EmployeeId emp1, EmployeeId emp2)
       {
           
return !emp1.Equals(emp2);
       }

   }

   [Serializable]
   
public class Employee
   {
      
private string name;
      
private decimal salary;
      
private readonly EmployeeId id;

      
public Employee(EmployeeId id, string name, decimal salary)
      {
         
this.id = id;
         
this.name = name;
         
this.salary = salary;
      }

      
public override string ToString()
      {
         
return String.Format("{0}: {1, -20} {2:C}", id.ToString(), name, salary);
      }
   }

 
class Program
   {
      
static void Main()
      {
         System.Threading.Thread.CurrentThread.CurrentCulture 
= new System.Globalization.CultureInfo("en-US");
         Dictionary
<EmployeeId, Employee> employees =
               
new Dictionary<EmployeeId, Employee>(31);

         EmployeeId idJeff 
= new EmployeeId("C7102");
         Employee jeff 
= new Employee(idJeff, "Jeff Gordon",
               
5164580.00m);
         employees.Add(idJeff, jeff);
         Console.WriteLine(jeff);

         EmployeeId idTony 
= new EmployeeId("C7105");
         Employee tony 
= new Employee(idTony, "Tony Stewart"4814200.00m);
         employees.Add(idTony, tony);
         Console.WriteLine(tony);

         EmployeeId idDenny 
= new EmployeeId("C8011");
         Employee denny 
= new Employee(idDenny, "Denny Hamlin"3718710.00m);
         employees.Add(idDenny, denny);
         Console.WriteLine(denny);

         EmployeeId idCarl 
= new EmployeeId("F7908");
         Employee carl 
= new Employee(idCarl, "Carl Edwards"3285710.00m);
         employees[idCarl] 
= carl;
         Console.WriteLine(carl);

         EmployeeId idMatt 
= new EmployeeId("F7203");
         Employee matt 
= new Employee(idMatt, "Matt Kenseth"4520330.00m);
         employees[idMatt] 
= matt;
         Console.WriteLine(matt);


         
while (true)
         {
            
try
            {
               Console.Write(
"Enter employee id (X to exit)> ");
               
string userInput = Console.ReadLine();
               userInput 
= userInput.ToUpper();
               
if (userInput == "X"break;
               EmployeeId id 
= new EmployeeId(userInput);

               Employee employee;
               
if (!employees.TryGetValue(id, out employee))
               {
                  Console.WriteLine(
"Employee with id {0} does not exist",
                     id);
               }
               
else
               {
                  Console.WriteLine(employee);
               }
            }
            
catch (Exception ex)
            {
               Console.WriteLine(
"Error: " + ex.Message);
            }
         }

      }

   }

9.7 HashSet

这个集合类包含了不重复项的无序列表。能够很方便的插入元素,该类还提供合集和交集。下面给出简单例子:

哈希集合demo
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Wrox.ProCSharp.Collections
{
   
class Program
   {
      
static void Main()
      {
         HashSet
<string> companyTeams = new HashSet<string>() { "Ferrari""McLaren""Toyota""BMW""Renault""Honda" };
         HashSet
<string> traditionalTeams = new HashSet<string>() { "Ferrari""McLaren" };
         HashSet
<string> privateTeams = new HashSet<string>() { "Red Bull""Toro Rosso""Spyker""Super Aguri" };

          
//添加没有重复,返回true
         if (privateTeams.Add("Williams"))
            Console.WriteLine(
"Williams added");
         
if (!companyTeams.Add("McLaren"))
            Console.WriteLine(
"McLaren was already in this set");

          
//子集
         if (traditionalTeams.IsSubsetOf(companyTeams))
         {
            Console.WriteLine(
"traditionalTeams is a subset of companyTeams");
         }

          
//超集
         if (companyTeams.IsSupersetOf(traditionalTeams))
         {
            Console.WriteLine(
"companyTeams is a superset of traditionalTeams");
         }

         traditionalTeams.Add(
"Williams");
          
//至少有一个元素相同
         if (privateTeams.Overlaps(traditionalTeams))
         {
            Console.WriteLine(
"at least one team is the same with the traditional and private teams");
         }


         HashSet
<string> allTeams = new HashSet<string>(companyTeams);
          
//添加全部内容到集合中去
         allTeams.UnionWith(privateTeams);
         allTeams.UnionWith(traditionalTeams);

         Console.WriteLine();
         Console.WriteLine(
"all teams");
         
foreach (var team in allTeams)
         {
            Console.WriteLine(team);
         }


          
//从集合中删除该集合中所有的元素
         allTeams.ExceptWith(privateTeams);
         Console.WriteLine();
         Console.WriteLine(
"no private team left");
         
foreach (var team in allTeams)
         {
            Console.WriteLine(team);
         }


      }
   }
}

9.8 位数组

当我们遇到位运算时候,可以使用BitArray类来处理,该类可以重新设置大小,不需要知道位数,类bitArray是一个引用类型,包含一个int数组,每32位使用一个新的整形。主要提供了,或 、与、异或操作,以及倒置位的值等方法,下面给出例子:

位数组demo
using System;
using System.Collections.Generic;
using System.Text;
using System.Collections;
using System.Collections.Specialized;

namespace BitArraySample
{
   
class Program
   {
      
static void BitArrayDemo()
      {
         BitArray bits1 
= new BitArray(8);
         bits1.SetAll(
true);
         bits1.Set(
1false);
         bits1[
5= false;
         bits1[
7= false;
         Console.Write(
"initialized: ");
         DisplayBits(bits1);
         Console.WriteLine();


         DisplayBits(bits1);
         bits1.Not();
         Console.Write(
" not ");
         DisplayBits(bits1);
         Console.WriteLine();

         BitArray bits2 
= new BitArray(bits1);
         bits2[
0= true;
         bits2[
1= false;
         bits2[
4= true;
         DisplayBits(bits1);
         Console.Write(
" or ");
         DisplayBits(bits2);
         Console.Write(
" : ");
         bits1.Or(bits2);
         DisplayBits(bits1);
         Console.WriteLine();


         DisplayBits(bits2);
         Console.Write(
" and ");
         DisplayBits(bits1);
         Console.Write(
" : ");
         bits2.And(bits1);
         DisplayBits(bits2);
         Console.WriteLine();

         DisplayBits(bits1);
         Console.Write(
" xor ");
         DisplayBits(bits2);
         bits1.Xor(bits2);
         Console.Write(
" : ");
         DisplayBits(bits1);
         Console.WriteLine();
      }

      
static void BitVectorDemo()
      {

         BitVector32 bits1 
= new BitVector32();
         
int bit1 = BitVector32.CreateMask();
         
int bit2 = BitVector32.CreateMask(bit1);
         
int bit3 = BitVector32.CreateMask(bit2);
         
int bit4 = BitVector32.CreateMask(bit3);
         
int bit5 = BitVector32.CreateMask(bit4);

         bits1[bit1] 
= true;
         bits1[bit2] 
= false;
         bits1[bit3] 
= true;
         bits1[bit4] 
= true;
         Console.WriteLine(bits1);

         bits1[
0xabcdef= true;
         Console.WriteLine(bits1);
         

         
int received = 0x79abcdef;

         BitVector32 bits2 
= new BitVector32(received);
         Console.WriteLine(bits2);
         
// sections: FF EEE DDD CCCC BBBBBBBB AAAAAAAAAAAA
         BitVector32.Section sectionA = BitVector32.CreateSection(0xfff);
         BitVector32.Section sectionB 
= BitVector32.CreateSection(0xff, sectionA);
         BitVector32.Section sectionC 
= BitVector32.CreateSection(0xf, sectionB);
         BitVector32.Section sectionD 
= BitVector32.CreateSection(0x7, sectionC);
         BitVector32.Section sectionE 
= BitVector32.CreateSection(0x7, sectionD);
         BitVector32.Section sectionF 
= BitVector32.CreateSection(0x3, sectionE);



         Console.WriteLine(
"Section A: " + IntToBinaryString(bits2[sectionA], true));
         Console.WriteLine(
"Section B: " + IntToBinaryString(bits2[sectionB], true));
         Console.WriteLine(
"Section C: " + IntToBinaryString(bits2[sectionC], true));
         Console.WriteLine(
"Section D: " + IntToBinaryString(bits2[sectionD], true));
         Console.WriteLine(
"Section E: " + IntToBinaryString(bits2[sectionE], true));
         Console.WriteLine(
"Section F: " + IntToBinaryString(bits2[sectionF], true));


      }

      
static string IntToBinaryString(int bits, bool removeTrailingZero)
      {
         StringBuilder sb 
= new StringBuilder(32);

         
for (int i = 0; i < 32; i++)
         {
            
if ((bits & 0x80000000!= 0)
            {
               sb.Append(
"1");
            }
            
else
            {
               sb.Append(
"0");
            }
            bits 
= bits << 1;
         }
         
string s = sb.ToString();
         
if (removeTrailingZero)
            
return s.TrimStart('0');
         
else
            
return s;
      }

      
static void Main()
      {
         BitVectorDemo();

         BitArrayDemo();

         
      }

  
      
static void DisplayBits(BitArray bits)
      {
         
foreach (bool bit in bits)
         {
            Console.Write(bit 
? 1 : 0);
         }
      }
   }
}

小结:

本章节讲了关于不同集合处理,数组的大小是固定的,但可以使用列表作为动态增加的集合。队列先进先出的方式访问元素,可以使用在很多特定场合。栈以后进先出的方式访问元素。链表可以快速的插入和删除元素,但是查询操作就慢很多。字典查询和插入操作比较快。hashSet可以用于无序的唯一项。位数组可以使用在二进制运算的特定场所。至于各个集合的性能问题,需要在使用时候特别考虑下。性能问题请参考yangjun写的C#六种集合性能比较