zoukankan      html  css  js  c++  java
  • 【读书笔记】C#高级编程 第十章 集合

    (一)概述

    数组的大小是固定的。如果元素个数是动态的,就应使用集合类。

    List<T>是与数组相当的集合类。还有其它类型的集合:队列、栈、链表、字典和集。

    (二)列表

    1、创建列表

    调用默认的构造函数,就可以创建列表对象。在泛型类List<T>中,必须为声明为列表的值指定类型。使用默认构造函数创建一个空列表。元素添加到列表后,列表的容量会扩大,每次添加元素达到容量上限后,容量将重新设置为原来的2倍。

    例子:

    List<string> strList = new List<string>();

    如果列表的容量改变了,整个集合就要重新分配到一个新的内存块中。使用Capacity属性可以获取和设置集合的容量。

    例子:

    strList.Capacity = 100;

    集合中的元素个数可以用Count属性读取。

    例子:

    var count= strList.Count;

    可以调用TrimExcess()方法去除不需要的容量。

    例子:

    strList.TrimExcess();

    但要注意的是,元素个数超过容量的90%,TrimExcess()方法就什么都不做。

    (1)集合初始值设定项

    还可以使用集合初始值设定项给集合赋值。

    例子:

    List<string> strList = new List<string>() { "zhangsan", "lisi" };

     

    (2)添加元素

    使用Add()方法可以给列表添加元素。

    例子:

    strList.Add("zhangsan");

    使用AddAange()方法,可以一次性给集合添加多个元素。

    例子:

    strList.AddRange(new string[] { "lisi", "wangmazi" });
    strList.AddRange(new List<string> { "zhaoliu", "qianqi" });

     

    (3)插入元素

    使用Insert()方法可以在指定位置插入元素。

    例子:

    strList.Insert(0, "begin");

    使用InsertRange()提供了插入大量元素的功能。

    例子:

    strList.InsertRange(1, new string[] { "zhang", "li", "wang" });

     

    (4)访问元素

    实现了IList<T>和IList接口的所有类都提供了一个索引器,可以使用索引器,通过传递元素号来访问元素。

    例子:

    var str0 = strList[0];

    处理使用foreach语句进行遍历以外,List<T>类还提供了了ForEach()方法,该方法返回void,参数为Action<T>。ForEach()方法遍历集合中的每一项,调用作为每一项的参数传递的方法。

    例子:

    List<string> strList = new List<string>();
    strList.Add("zhangsan");
    strList.AddRange(new string[] { "lisi", "wangmazi" });
    strList.AddRange(new List<string> { "zhaoliu", "qianqi" });
    strList.ForEach(Console.WriteLine);
    Console.ReadKey();

    运行以上代码,结果如下:

     

    Console.WriteLine()方法作为参数传递给ForEach()方法。

    (5)删除元素

    删除元素时,可以利用索引,也可以传递要删除的元素。

    例子:

    strList.RemoveAt(1);

    按索引删除比较快。

    RemoveRange()方法可以从集合中删除许多元素。

    strList.RemoveRange(0, 1);

    还可以使用RemoveAll()方法删除有指定特定的所有元素,还可以使用ICollection<T>接口定义的Clear()方法。

    (6)搜索

    可以使用IndexOf()、LastIndexOf()、FindIndex()、FindLastIndex()、Find()、FindLast()。如果只检查元素是否存在,List<T>类提供了Exists()方法。

    例子:

    int index = strList.IndexOf("lisi");

     

    (7)排序

    List<T>类可以使用Sort()方法对元素排序。Sort()方法使用快速排序算法,比较所有的元素,值到整个列表排好序位为止。

    (8)类型转换

    使用List<T>类的ConvertAll<TOutput>()方法,可以把所有类型的集合转换为另一种类型。

    例子:

    List<object> objList = strList.ConvertAll<object>(s => s);

     

    2、只读集合

    List<T>集合中的AsReadOnly()方法返回ReadOnlyCollection<T>类型的对象。

    (三)队列

    队列(Queue<T>)是已先进先出(FIFO)的方式来处理的集合。队列只允许在队列中添加元素,该元素会放在队列的末尾(Enqueue()方法),从队列头部获取元素(使用Dequeue()方法)。

    例子:

    Queue<string> qu = new Queue<string>();
    qu.Enqueue("zhangsan");
    qu.Enqueue("lisi");
    qu.Enqueue("wangmazi");
    var dqu = qu.Dequeue();
    Console.WriteLine(dqu);

    运行以上代码,结果如下:

     

    需要注意的是Dequeue()方法获取元素后会从队列中删除该元素,使用Peek()方法则仅获取不删除。使用枚举器不会改变元素的状态。因为Queue<T>类没有实现IList<T>接口,所以不能用索引器访问队列。

    (四)栈

    栈(Stack<T>)与队列(Queue<T>)很相似,只是最后添加到栈中的元素最先读取。栈是一个后进先出(LIFO)的容器。栈用Push()方法添加元素,用Pop()方法获取最近的元素。

    例子:

    Stack<string> st = new Stack<string>();
    st.Push("zhangsan");
    st.Push("lisi");
    st.Push("wangmazi");
    var pst = st.Pop();
    Console.WriteLine(pst);

    运行以上代码,结果如下:

     

    需要注意的是Pop()方法获取元素后会从队列中删除该元素,使用Peek()方法则仅获取不删除。使用枚举器不会改变元素的状态。

    (五)链表

    LinkedList<T>是一个双向链表,其元素指向它前面和后面的元素。链表的优点是,插入列表中间位置会很快。缺点是只能一个一个访问。链表不能在列表中仅存储值,其还会包含上一个和下一个元素的信息。

    LinkedList<T>类定义的成员可以访问链表中的第一个和最后一个元素(First和Last)、在指定位置插入元素(AddAfter()、AddBefore()、AddFirst()和AddLast()方法),删除指定位置元素(Remove()、RemoveFirst()和RemoveLast()方法)、从链表的开头(Find()方法)或结尾(FindLast()方法)开始搜索元素。

    例子:

    LinkedList<int> list = new LinkedList<int>();
    list.AddFirst(2);
    list.AddFirst(1);
    list.AddLast(5);
    list.AddLast(6);
    list.AddAfter(list.Find(2),3);
    list.AddBefore(list.Find(5),4);
    foreach (var item in list)
    {
        Console.WriteLine(item);
    }
    list.RemoveLast();
    Console.WriteLine("移除末尾元素后:");
    foreach (var item in list)
    {
        Console.WriteLine(item);
    }

    运行以上代码,结果如下:

     

     

    (六)有序列表

    如果需要基于键对所需集合排序,就可以使用SortedList<TKey,TValue>类。这个类按照键给元素排序。

    例子:

    SortedList<string, string> list = new SortedList<string, string>();
    list.Add("b", "lisi");
    list.Add("a", "zhangsan");
    list.Add("c","wangmazi");
    foreach (var item in list)
    {
        Console.WriteLine("排序位置{0},是{1}",item.Key,item.Value);
    }

    运行以上代码,结果如下:

     

     

    (七)字典

    字典也成为映射或散射列表。字典的主要特性是能根据键快速查找值。也可以自由添加和删除元素,这有点像List<T>类,但没有在内存中移动后续元素的性能开销。

    1、键的类型

    用作字典的键必须重写Object类的GetHashCode()方法,并实现IEquatable<T>.Equals()方法,或重写Object类的Equals()方法。也可以实现IEqualityComparer<T>接口的GetHashCode()方法和Equals()方法。

    需要注意的是,如果A.Equals(B)方法返回true,则A.GetHashCode()B.GetHashCode()方法必须总是返回相同的散列代码。

    字符串比Int32更适合用作键。

    2、字典示例

    Dictionary<string, string> dic = new Dictionary<string, string>();
    dic.Add("a", "zhang");
    dic.Add("b", "li");
    dic.Add("c", "wang");
    foreach (var item in dic)
    {
        Console.WriteLine(item);
    }

     

    3、Lookup类

    Lookup<TKey,TElement>类非常类似于Dictionary<TKey,TValue>类,但把键映射到一个值集上。Lookup<TKey,TValue>类不能像一般的字典那样创建,而必须调用ToLookup()方法,该方法返回一个Lookup<TKey,TElement>对象。调用ToLookup()方法需要传递一个Func<TSource,TKey>类型的委托来定义键的选择器。

    例子:

    var personList = new List<Person>();
    personList.Add(new Person() { Name = "zhangsan", Age = 13 });
    personList.Add(new Person() { Name = "lisi", Age = 15 });
    personList.Add(new Person() { Name = "wangmazi", Age = 13 });
    personList.Add(new Person() { Name = "zhaoliu", Age = 18 });
    var personLookup = personList.ToLookup(p => p.Age);
    foreach (var item in personLookup[13])
    {
        Console.WriteLine(item.Name);
    }

     

    4、有序字典

    SortedDictionary<TKey,TValue>类是一个二叉搜索树,其中的元素根据键来排序。该键类型必须实现IComparable<TKey>接口,在没有实现的情况下,可以传递一个实现了IComparer<TKey>接口的比较器。

    • SortedList<TKey,TValue>类使用的内存比SortedDictionary<TKey,TValue>类少。
    • SortedDictionary<TKey,TValue>类的元素插入和删除操作比较快。
    • 在用已排好序的数据填充集合时,若不需要修改容量,SortedList<TKey,TValue>类就比较快。

     

     

    (八)集

    包含不重复的集合成为“集(set)”。.NET Framework包含两个集(HashSet<T>和SortedSet<T>),它们都实现ISet<T>接口。HashSet<T>集包含不重复元素的无序列表,SortedSet<T>包含不重复的有序列表。

    例子:

     1 var firstNameSet1 = new HashSet<string>();
     2 firstNameSet1.Add("zhang");
     3 firstNameSet1.Add("zhang");
     4 firstNameSet1.Add("li");
     5 firstNameSet1.Add("wang");
     6 firstNameSet1.Add("zhao");
     7 foreach (var item in firstNameSet1)
     8 {
     9     Console.WriteLine(item);
    10 }
    11 var firstNameSet2 = new HashSet<string>();
    12 firstNameSet2.Add("Smith");
    13 firstNameSet2.Add("Johnson");
    14 firstNameSet2.Add("Williams");
    15 firstNameSet2.Add("Jones");
    16 var firstNameSet = new SortedSet<string>();
    17 firstNameSet.UnionWith(firstNameSet1);
    18 firstNameSet.UnionWith(firstNameSet2);
    19 Console.WriteLine("将两个无序集合并到有序集:");
    20 foreach (var item in firstNameSet)
    21 {
    22     Console.WriteLine(item);
    23 }

    运行以上代码,结果如下:

     

     

     

    (九)可观察的集合

    如果需要集合中的元素何时删除或添加的信息,就可以使用ObservableCollection<T>类。

    例子:

     1 static void Main(string[] args)
     2 {
     3     ObservableCollection<string> obs = new ObservableCollection<string>();
     4     obs.CollectionChanged += Obs_changeInfo;
     5     obs.Add("1");
     6     obs.Add("2");
     7     Console.ReadKey();
     8 }
     9 
    10  
    11 private static void Obs_changeInfo(object sender, NotifyCollectionChangedEventArgs e)
    12 {
    13     Console.WriteLine("触发事件操作方法:{0}",e.Action);
    14     var targetElement = e.NewItems == null ? e.OldItems : e.NewItems;
    15     foreach (var item in targetElement)
    16     {
    17         Console.WriteLine("触发元素:{0}", item);
    18     }
    19 }

    运行以上代码,结果如下:

     

     

     

    (十)位数组

    如果需要处理的数字有许多位,就可以使用BitArray类和BitVector32结构。(完全不知道这个有什么作用,暂不做笔记)

     

     

    (十一)不变的集合

    如果对象可以改变其状态,就很难在多个同时运行的任务中使用。先填充集合,再将它变为不变的数组会更高效。需要进行一些处理时,可以再次使用可变的集合。此时可以使用不变集合提供的构建器类。

    例子:

    List<string> list = new List<string>();
    list.Add("zhang");
    list.Add("li");
    var immutableList = list.ToImmutableList();//可变集合转为不变集合
    var immutableListBuilder = immutableList.ToBuilder();//重新转换为可变集合
    immutableListBuilder.Add("wang");
    immutableList = immutableListBuilder.ToImmutable();//可变集合转为不变集合
    foreach (var item in immutableList)
    {
        Console.WriteLine(item);
    }

    运行以上代码,结果如下:

     

    只读集合提供集合的只读试图。在不使用只读试图访问集合的情况下,该集合仍可修改。而不变集合无法修改。

     

     

    (十二)并发集合

    从.NET 4开始,.NET中名称空间System.Collections.Concurrent中提供了几个线程安全的集合类。为了对集合进行线程安全的访问,定义了IProducerConsumerCollection<T>接口。这个接口中最重要的方法是TryAdd()方法和TryTake()方法。

    并发集合参考:http://blog.csdn.net/wangzhiyu1980/article/details/45497907

  • 相关阅读:
    Working with macro signatures
    Reset and Clear Recent Items and Frequent Places in Windows 10
    git分支演示
    The current .NET SDK does not support targeting .NET Core 2.1. Either target .NET Core 2.0 or lower, or use a version of the .NET SDK that supports .NET Core 2.1.
    Build website project by roslyn through devenv.com
    Configure environment variables for different tools in jenkins
    NUnit Console Command Line
    Code Coverage and Unit Test in SonarQube
    头脑王者 物理化学生物
    头脑王者 常识,饮食
  • 原文地址:https://www.cnblogs.com/dlxh/p/6661965.html
Copyright © 2011-2022 走看看