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类提供了列表中的元素发生变化时触发的事件。
 

 

转载于:https://www.cnblogs.com/crazytomato/p/7424434.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值