zoukankan      html  css  js  c++  java
  • C#高级编程(第9版) 第10章 集合 笔记

    话说 虽然敲过好多代码, 但除了C++,一直没正眼瞧过其它语言。(没办法 谁叫C++既有oop又能自由控制内存呢)

    今天 看公司老项目的src,c#的,linq+Dictionary的用法有感。所以找来C#的资料 就学了一下,妈的 变天儿了。

    以后不能再用C++编写思路,囫囵着过日子了。

    ------------------------------------------------------------------ 我是分割线 -----------------------------------------------------------------------

    以下 学习笔记

    列表List<T>、队列、栈、链表、字典和集;
    位数组 和 并发集合 【多线程环境使用】
     
    集合借口和类型
    大多数集合类在System.Collections 和 System.Collections.Generic命名空间中。
    泛型集合 ==> System.Collections.Generic
    专用于特定类型的集合类 ==> System.Collections.Concurrent
    不可变的集合类 ==> System.Collections.Immutable
     
    列表
    .NET Framework为动态列表提供了泛型类 List<T>。
    这个类实现了IList、ICollection、IEnumerable、IList<T>、ICollection<T>和IEnumerable<T>接口。
     
    ArrayList是一个非泛型列表,它可以将任意Object类型作为其元素。
     
    使用默认的构造函数创建一个空列表。元素添加到列表中后,列表的容量就会扩大为可容纳4个元素。如果添加了第5个元素,列表的大小就成新设置为包含8个元素,如果8个元素还不够,列表的大小就重新设置为包含16个元素。每次都会将列表的容量重新设置为原来的2倍。
    如果列表的内容改变了,整个集合就要重新分配到一个新的内存块中。即创建一个新的数组,大小是原来的2倍。
     
    创建指定元素个数的列表
    List<int> intList = new List<int>(10);
    
     
    可以通过索引访问的集合类有 ArrayList、StringCollection 和 List<T>
     
    ForEach()方法遍历集合中的每一项,调用作为每一项的参数传递的方法。
    public delegate void Action<T> (T obj);
    public void ActionHandler (Racer obj);
    racers.ForEach( Console.WriteLine );
    racers.ForEach( r => Console.WriteLine("{0:A}", r); //lambda表达式
    
    删除元素
    删除元素时,可以利用索引,也可以传递要删除的元素。
    按索引删除比较快,因为必须在集合中搜索要删除的元素。Remove()方法现在集合中搜索,用IndexOf()方法获取元素的索引,在使用该索引删除元素。IndexOf()方法先检查元素类型是否实现了IEquatable<T>接口。如果是就调用这个接口的Equals()方法,确定集合中的元素是否等于传递给Equals()方法的元素。如果没有实现这个接口,就使用Object类的Equals()方法比较这些元素。Object类中的Equals()方法的默认实现代码对值类型进行比较,对引用类型只比较其引用。
    重写IEquatable<T>接口或Object.Equals()方法可以根据列表元素对象的特定属性进行删除列表元素。
    第7章 介绍如何重写Equals()方法
     
    搜索
    有不同的方式在集合中搜索元素。可以获得要查找的元素的引用,或者搜索元素本身。
    方法有:
    IndexOf()
    LastIndexOf()
    FindIndex()
    FindLastIndex()
    Find()
    FindLast()
    检查元素是否存在:List<T>.Exists()方法;
    
    IndexOf()可以指定不需要搜索整个集合,需要指定开始索引以及需要迭代的元素个数
    FindIndex()可以搜索有某个特性的元素
    public int FindIndex(Predicate<T> match); //Predicate<T>类型是一个委托,该委托返回一个bool值
    int index = racers.FindIndex( r => r.Country == "FinLand");
     
    FindIndex()方法返回所查找元素的索引。Find()方法除了获得索引之外,还可以直接获得集合中的元素。
    Racer racer = racers.Find( r => r.FirstName == "Niki");
    要获得于Predicate<T>类型匹配的所有项,而不是一项,可以使用FindAll()方法;
     
    排序
    List<T>类可以使用Sort()方法对元素排序。Sort()方法使用快速排序算法。
    Sort()方法使用了几个重载的方法:
    public void List<T>.Sort();
    public void List<T>.Sort(Comparison<T>);
    public void List<T>.Sort(IComparer<T>);
    public void List<T>.Sort(Int32, Int32, IComparet<T>);
    

    1. 只有集合中的元素实现了IComparable接口,才能使用不带参数的Sort()方法;

    2. 如果需要按照元素类型不默认支持的方式排序,就应使用其他技术,如传递一个实现了IComparer<T>接口的对象。
    3. 排序的另一种方式使用重载的Sort()方法。
    调用Reverse()方法,逆转整个集合的顺序。
     
    类型转换
    使用List<T>类的ConvertAll<TOutput>()方法,可以把所有类型的集合转换为另一种类型。
    ConvertAll<TOutput>()方法使用一个Converter委托:
    public sealed delegate TOutput Converter<TInput, TOutput> (TInput from);
    eg:
    List<Person> persons = racers.ConvertAll<Person>( r => new Person(r.FirstName + " " + r.LastName));
    
    只读集合
    List<T>集合的AsReadOnly()方法返回 ReadOnlyCollection<T>类型的对象。
    ReadOnlyCollection<T>类实现的接口与List<T>集合相同,但如若修改将抛出异常NotSupportedException异常。
    ReadOnlyCollection<T>还实现了IReadOnlyCollection<T>和IReadOnlyList<T>接口。
     
     
    队列
    Queue<T> 命名空间 System.Collections.Generic
    队列是其元素以先进先出(FIFO)的方式来处理的集合。
     
    可以使用多个队列,一个队列对应一个优先级。打印队列和线程队列是这样的。
    可以为一组队列建立一个数组,数组中的一项代表一个优先级。
     
    在内部Queue<T>类使用T类型的数组;它实现ICollection和IEnumerable<T>接口。
    因为没有实现IList<T>接口,所以不能用索引器访问元素。
    Queue<T>方法如下:
    Count
    Enqueue
    Dequeue
    Peek //从队列头部读取一个元素但不删除它
    TrimExcess //重新设置队列的容量,Dequeue()方法从队列中删除元素,但不会重新设置队列容量,要从队列的头部去除空元素,应使用TrimExcess()方法
    

    多线程可以同时访问,但要是用lock语句锁定队列的访问:

    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; }
    	}
    }
    
    栈是一个后进先出(LIFO)的容器
    与Queue<T>类相同,Stack<t>类实现了IEnumerable<T>和ICollection接口。
    成员列表:
    Count
    Push
    Pop
    Peek
    Contains
    
    在foreach方法中,使用IEnumerable接口迭代所有的元素;栈的枚举器不会删除元素,它只是逐个返回元素
     
    链表
    LinkedList<T>是一个双向链表
    链表的有点事,如果将元素插入列表的中间位置,使用链表就会非常快。
    在插入一个元素时,只需要修改上一个元素的Next引用和下一个元素的Previous引用,使它们引用所插入的元素。
    在List<T>类中,插入一个元素时,需要移动该元素后面的所有元素。
    链表的缺点:链表的元素只能一个接一个地访问,这需要较长的时间来查找位于链表中间或尾部的元素。
     
    有序列表
    如果需要基于键对所需集合排序,就可以使用SortList<TKey, TValue>类。这个类按照键给元素排序。
     
    字典
    .NET Framework提供了几个字典类,可以使用的最主要的类是Dictionary<TKey, TValue>
     
    键的类型
    用作字典中键的类型必须重写Object类的GetHashCode()方法;字典的性能取决于GetHashCode()方法的实现代码。
    除了实现GetHashCode()方法之外,键类型还必须实现IEquatable<T>.Equals()方法,或者重写Object类的Equals()方法。
     
    Lookup 类
    Lookup<TKey, TElement>把键映射到一个值集上;Lookup<TKey, TElement>类在程序集System.Core中实现,用System.Linq命名空间。
     
    Lookup<TKey, TElement>类不能像一般的字典那样创建,而必须调用ToLookup()方法,该方法返回一个Lookup<TKey, TElement>对象。ToLookup()方法是一个扩展方法,它可以用于实现了IEnumerable<T>接口的所有类。
     
    有序字典
    SortedDictionary<TKey, TValue>类是一个二叉搜索树,其中的元素根据键来排序。该键类型必须实现IComparable<TKey>接口。
    如果键的类型不能排序,则还可以创建一个实现了IComparer<TKey>接口的比较器,将比较器用作有序字典的构造函数的一个参数。
     
     
    包含不重复元素的集合成为“集(set)”。
    .NET Framework包含两个集 HashSet<T> 和 SoredSet<T>, 它们都实现ISet<T>接口。
     
    可观察的集合
    如果需要集合中的元素何时删除或添加的信息,就可以使用ObservableCollection<T>类。
    这个类是为WPF定义的,这样UI就可以得知集合的变化,因此这个类在程序集WindowsBase中定义;命名空间是 System.Collections.ObjectModel。
    ObservableCollection<T>类派生自Collection<T>基类,该基类可以创建自定义集合,并在内部使用List<T>类。重写基类中的虚方法SetItem()和RemoveItem(),以触发CollectionChanged事件。这个类的用户可以使用INotifyCollectionChanged接口注册这个事件。
     
    位数组
    如果需要处理的数字有许多位,就可以使用BitArray类和BitVector32接口。
     
    BitArray类
    BitArray类位于名称空间System.Collections中,BitArray类可以重新设置大小,如果事先不知道需要的位数,就可以使用BitArray类,它可以包含非常多的位。
    BitArray类是一个引用类型,它包含一个int数组,其中每32位使用一个新整数。
    成员如下:
    CountLength
    Item [Get / Set]
    SetAll
    Not //取反
    And / Or / Xor
    
    辅助方法DisplayBits()遍历BitArray:
    static void DisplayBits(BitArray bits) {
        foreach (bool bit in bits) {
            Console.Write(bit ? 1 : 0);
        }
    }
    
    var bits = new BitArray(8); //创建一个8位的数组
    bits.SetAll(true); //把8位都设置位true,即 1
    bits.Set(1, false); //把下标为1的位设置为false,即 0
    bits[5] = false; //使用索引器设置
    Console.Write("Initialized:");
    DisplayBits(bits);
    Console.WriteLine();
    

    BitVector32结构
    BitVector32结构位于命名空间System.Collections.Specialized中。
    BitVector32结构是基于栈的,因此比较快。但仅能包含32位,存储在一个整数中。
     
    如果事先知道需要的位数,就可以使用BitVector32结构替代BitArray类,效率较高;因为他是一个值类型。
    一个整数可以存储32位,如果需要更多的位,可以使用多个BitVector32值或BitArray类。
    BitVector32成员如下:
    Data //Data属性把BitVector32结构中的数据返回为整数
    Item //BitVector32的值可以使用索引器设置
    CreateMask //这是一个静态方法,用于为访问BitVector32结构中的特定位创建掩码
    CreateSection //这是一个静态方法,用于创建32位中的几个片段
    
     
    不变的集合
    如果对象可以改变其状态,就很难在多个同时运行的任务中使用。这些集合必须同步。如果对象不能改变其状态,就很容易在多个线程中使用。不能改变的对象成为不变的对象。
    在VS2013中,Microsoft提供了一个新的集合库: Microsoft Immutable Collections。它包含不变的集合类 ----- 创建后就不能改变的集合类。
     
    并发集合
    从 .NET 4开始,命名空间System.Collections.Concurrent中提供了几个线程安全的集合类。
    线程安全的集合可防止多个线程以相互冲突的方式访问集合。
    为了对集合进行线程安全的访问,定义了IProducerConsumerCollection<T>接口。这个接口中最重要的方法是TryAdd()和TryTake()。TryAdd()方法尝试给集合添加一项,但如果集合禁止添加项,这个操作就可能失败。TryTake()雷同。
     
    下面列出System.Collections.Concurrent名称空间中的类及功能:
    ConcurrentQueue<T>
    ConcurrentStack<T>
    ConcurrentBag<T>
    ConcurrentDictionary<TKey, TValue>
    BlockingCollection<T>
    
    ConcurrentXXXX集合是线程安全的,如果某个动作不适用于线程当前状态,他们返回false。
    BlockingCollection<T>是对实现IProducerConsumerCollection<T>接口的任意类的修饰器,他默认使用ConcurrentQueue<T>类。
     
    创建管道
    将这些并发集合类用于管道是一种很好的应用:一个任务向一个集合类写入一些内容,同时另一个任务从该集合中读取内容。
     
    性能
    许多集合类都提供了相同的功能,但是,其性能常常有很大的区别。
    在MSDN文档中,集合的方法常常有性能提示,给出了以大写O标记的操作时间:
    O(1) //表示无论集合中有多少数据项,这个操作需要的时间都不变
    O(long n) //表示对于集合执行一个操作需要的事件在最坏情况是是N
    O(n) //表示操作需要的时间随集合中元素的增加而增加,但每个元素需要增加的时间不是线性的,而是呈对数曲线
     
     
    总结
    数组Array的大小是固定的,可以使用列表List作为动态增长的集合。
    队列Queue以先进先出的方式访问元素,栈Stack以先进后出的方式访问元素。
    链表LinkedList可以快速的插入和删除元素,但搜索操作比较慢。
    通过键值对可以使用字典Dictionary,它的搜索和插入操作比较快。
    集用于唯一项,可以是无序的HashSet,也可以是有序的SortedSet。
    ObservableCollection类提供了列表中的元素发生变化时触发的事件。
     
  • 相关阅读:
    java8学习之Optional深入详解
    java8学习之Supplier与函数式接口总结
    java8学习之Predicate深入剖析与函数式编程本质
    conda
    matplotlib-折线图、散点图
    欧盟GDPR通用数据保护条例-原文
    python装饰器的应用案例
    python练习汇总
    python分段算利润、税收
    需求-shidebing
  • 原文地址:https://www.cnblogs.com/crazytomato/p/7424434.html
Copyright © 2011-2022 走看看