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


    当元素的个数是动态的,就应使用集合类,集合类一般有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#六种集合性能比较

    本节代码下载

  • 相关阅读:
    31天重构学习笔记28. 为布尔方法命名
    .NET 技术社区之我见(中文篇)
    31天重构学习笔记26. 避免双重否定
    31天重构学习笔记25. 引入契约式设计
    31天重构学习笔记20. 提取子类
    31天重构学习笔记18. 使用条件判断代替异常
    31天重构学习笔记19. 提取工厂类
    31天重构学习笔记24. 分解复杂判断
    31天重构学习笔记23. 引入参数对象
    31天重构学习笔记17. 提取父类
  • 原文地址:https://www.cnblogs.com/ylwn817/p/2067477.html
Copyright © 2020-2023  润新知