C#高级编程9-第10章 集合
集合
1.集合接口和类型
接口 |
说明 |
IEnumerable<T> |
如果foreach语句用于集合,就需要IEnumerable接口.这个借口定义了方法GetEnumerator(),他返回一个实现了IEnumerator接口的枚举 |
ICollection<T> |
ICollection<T>接口有泛型集合类实现.使用这个借口可以获得集合中的元素个数(Count属性),把集合复制到数组中(CopyTo()方法),还可以从集合中添加和删除元素(Add(),Remove(),Clear()) |
List<T> |
IList<T>接口用于可通过位置访问其中的元素列表,这个接口定义了一个 索引器,可以在集合的指定位置插入或删除 mount些项(Insert()和Remove()方法).IList<T>接口派生自ICollection<T>接口 |
ISet<T> |
ISet<T>接口是.NET4中新增的.实现这个接口的集允许合并不同的集.获得两个集的交集,检查两个集合是否重叠.ISet<T>接口派生自ICollection<T>接口 |
IDictionary<TKey,TValue> |
IDictionary<TKey,TValue>接口由包含键和值的泛型集合类 实现.使用这个接口可以访问所有的键和值,使用键类型的索引器可以访问某些项,还可以添加或删除某些项 |
ILookup<TKey,TValue> |
ILookup<TKey,TValue>接口类似于IDictionary<TKey,TValue>接口,实现该接口的集合有键和值,且可以通过一个键包含多个值 |
IComparer<T> |
接口ICommparer<T>由比较器实现,通过Comparer()方法给集合中的元素排序 |
IEqualityComparer<T> |
接口IEqualityComparer<T>由一个比较器实现,该比较器可用于字典中的键.使用这个接口,可以对对象进行相等性比较.在.NET中,这个接口也由数组和元组实现 |
IProducerConsumerColllection<T> |
IProducerConsumerCollection<T>接口是.NET4中新增的,它支持新的线程安全的集合类 |
IReadOnlyList<T>、 IReadOnlyDictionary<T>、 IReadOnlyCollection<T> |
初始化后不能修改的集合,只能检索对象,不能添加和删除. |
2.列表
先看看一个实例:
[Serializable]
public class Racer : IComparable<Racer>, IFormattable
{
public int Id { get; private set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Country { get; set; }
public int Wins { get; set; } public Racer(int id, string firstName, string lastName, string country)
: this(id, firstName, lastName, country, wins: )
{
}
public Racer(int id, string firstName, string lastName, string country, int wins)
{
this.Id = id;
this.FirstName = firstName;
this.LastName = lastName;
this.Country = country;
this.Wins = wins;
} public override string ToString()
{
return String.Format("{0} {1}", FirstName, LastName);
} public string ToString(string format, IFormatProvider formatProvider)
{
if (format == null) format = "N";
switch (format.ToUpper())
{
case null:
case "N": // name
return ToString();
case "F": // first name
return FirstName;
case "L": // last name
return LastName;
case "W": // Wins
return String.Format("{0}, Wins: {1}", ToString(), Wins);
case "C": // Country
return String.Format("{0}, Country: {1}", ToString(), Country);
case "A": // All
return String.Format("{0}, {1} Wins: {2}", ToString(), Country, Wins);
default:
throw new FormatException(String.Format(formatProvider,
"Format {0} is not supported", format));
}
} public string ToString(string format)
{
return ToString(format, null);
} public int CompareTo(Racer other)
{
if (other == null) return -;
int compare = string.Compare(this.LastName, other.LastName);
if (compare == )
return string.Compare(this.FirstName, other.FirstName);
return compare;
}
}
创建列表
使用默认的构造函数创建一个空列表,元素添加到列表后,列表容量会扩大到可接纳4个元素。
如果添加了第5个元素,列表大小会重新设置为8个元素。每次都会将列表的容量重新设置为原来的2倍.
var intList=new List<int>();
如果列表的容量变了,整个集合就要重新分配到新的内存块中,我们可以在初始化时设置它的容量:
List<int> intList=new List<int>();
如果列表的个数超过10个,可以设置容量Capacity:
intList.Capacity = ;
如果列表的元素已经添加完了,列表会存在多余的容量空间。可以使用TrimExcess方法去除不要的容量:
intList.TrimExcess();
a.集合初始值设定项
使用初始化构造器初始化设定项
int[] arr = { , , };
var intList = new List<int>(arr) ;
括号中初始化
var intList = new List<int>() { , };
b.添加元素
intList.Add();
添加数组
intList.AddRange(new int[] { , });
c.插入元素
intList.Insert(, );
d.访问元素
使用索引获取:
var value = intList[];
循环遍历:
foreach (var item in intList)
{
var res = item;
}
forEach方法:
class List<T> : IList<T>
{
private T[] items;
public void forEach(Action<T> action)
{
if (action == null) throw new ArgumentNullException("action");
foreach (var item in items)
{
action(item);
}
}
}
然后我们可以这样调用:
intList.ForEach(m => Console.WriteLine(m));
e.删除元素
按索引删除,比较快
intList.RemoveAt();
按元素值删除
intList.Remove();
f.搜索
在集合中搜索元素。可以查找索引和元素。
FindIndex通过匹配元素值,获得索引:
intList.FindIndex(m => m==);
FindIndex方法参数Predicate<T>传入匹配的表达式,返回匹配的元素索引值,Predicate<T>委托表示定义一组条件并确定指定对象是否符合这些条件的方法
intList.Find(m => m == );
intList.FindAll(m => m > );
Find返回了匹配条件的元素值,FindAll返回了匹配条件的所有元素
g.排序
列表使用Sort方法进行元素排序
intList.Sort();
intList.Sort((m, n) => m);
Sort(Comparison<T> comparison)方法参数中的委托Comparison含有2个参数,方法将这2个元素进行比较,然后返回绝对值,如果返回-1的,元素需要排前面,返回1的元素需要排后面.
Sort(IComparer<T> comparer)方法参数中是一个比较接口,接口实现Comparer方法
public enum CompareType
{
FirstName,
LastName,
Country,
Wins
} public class RacerComparer : IComparer<Racer>
{
private CompareType compareType;
public RacerComparer(CompareType compareType)
{
this.compareType = compareType;
} public int Compare(Racer x, Racer y)
{
if (x == null && y == null) return ;
if (x == null) return -;
if (y == null) return ; int result;
switch (compareType)
{
case CompareType.FirstName:
return string.Compare(x.FirstName, y.FirstName);
case CompareType.LastName:
return string.Compare(x.LastName, y.LastName);
case CompareType.Country:
result = string.Compare(x.Country, y.Country);
if (result == )
return string.Compare(x.LastName, y.LastName);
else
return result;
case CompareType.Wins:
return x.Wins.CompareTo(y.Wins);
default:
throw new ArgumentException("Invalid Compare Type");
}
}
}
h.类型转换
Converter委托
public delegate TOutput Converter<in TInput, out TOutput>(TInput input);
ConvertAll可以将一种类型的集合转换为另一种类型的集合。
intList.ConvertAll(m => m.ToString());
只读集合
创建集合后,它们是只读的。
3.队列
代表了一个先进先出的对象集合。当您需要对各项进行先进先出的访问时,则使用队列。当您在列表中添加一项,称为入队,当您从列表中移除一项时,称为出队。
添加队列元素时加上lock,因为多线程可以同时访问,所以对队列进行锁定访问。
using System;
using System.Collections; namespace CollectionsApplication
{
class Program
{
static void Main(string[] args)
{
Queue q = new Queue(); q.Enqueue('A');
q.Enqueue('M');
q.Enqueue('G');
q.Enqueue('W'); Console.WriteLine("Current queue: ");
foreach (char c in q)
Console.Write(c + " ");
Console.WriteLine();
q.Enqueue('V');
q.Enqueue('H');
Console.WriteLine("Current queue: ");
foreach (char c in q)
Console.Write(c + " ");
Console.WriteLine();
Console.WriteLine("Removing some values ");
char ch = (char)q.Dequeue();
Console.WriteLine("The removed value: {0}", ch);
ch = (char)q.Dequeue();
Console.WriteLine("The removed value: {0}", ch);
Console.ReadKey();
}
}
}
当上面的代码被编译和执行时,它会产生下列结果:
Current queue:
A M G W
Current queue:
A M G W V H
Removing values
The removed value: A
The removed value: M
class Program
{
static void Main()
{
var dm = new DocumentManager(); ProcessDocuments.Start(dm); // Create documents and add them to the DocumentManager
for (int i = ; i < ; i++)
{
Document doc = new Document("Doc " + i.ToString(), "content");
dm.AddDocument(doc);
Console.WriteLine("Added document {0}", doc.Title);
Thread.Sleep(new Random().Next());
} }
}
Program
public class ProcessDocuments
{
public static void Start(DocumentManager dm)
{
Task.Factory.StartNew(new ProcessDocuments(dm).Run);
} protected ProcessDocuments(DocumentManager dm)
{
if (dm == null)
throw new ArgumentNullException("dm");
documentManager = dm;
} private DocumentManager documentManager; protected void Run()
{
while (true)
{
if (documentManager.IsDocumentAvailable)
{
Document doc = documentManager.GetDocument();
Console.WriteLine("Processing document {0}", doc.Title);
}
Thread.Sleep(new Random().Next());
}
}
}
ProcessDocuments
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 > ;
}
}
}
DocumentManager
public class Document
{
public string Title { get; private set; }
public string Content { get; private set; } public Document(string title, string content)
{
this.Title = title;
this.Content = content;
}
}
Document
4.栈
代表了一个后进先出的对象集合。当您需要对各项进行后进先出的访问时,则使用堆栈。当您在列表中添加一项,称为推入元素,当您从列表中移除一项时,称为弹出元素。
class Program
{
static void Main()
{
var alphabet = new Stack<char>();
alphabet.Push('A');
alphabet.Push('B');
alphabet.Push('C'); Console.Write("First iteration: ");
foreach (char item in alphabet)
{
Console.Write(item);
}
Console.WriteLine(); Console.Write("Second iteration: ");
while (alphabet.Count > )
{
Console.Write(alphabet.Pop());
}
Console.WriteLine(); }
}
Program
First iteration: CBA
Second iteration: CBA
5.链表
LinkedList<T>是一个双向链表,其元素指向它前面和后面的元素,这样通过移动下一个元素就可以正向遍历整个链表。通过移动到前一个元素可以反向遍历这个链表
链表的优点是,如果将元素插入列表的中间位置,使用链表会很快,在插入一个元素时,只需要修改上一个元素的Next引用和下一个元素的Previous引用,使他们引用所插入的元素。
public class Document
{
public string Title { get; private set; }
public string Content { get; private set; }
public byte Priority { get; private set; } public Document(string title, string content, byte priority)
{
this.Title = title;
this.Content = content;
this.Priority = priority;
}
}
Document
public class PriorityDocumentManager
{
private readonly LinkedList<Document> documentList; // priorities 0.9
private readonly List<LinkedListNode<Document>> priorityNodes; public PriorityDocumentManager()
{
documentList = new LinkedList<Document>(); priorityNodes = new List<LinkedListNode<Document>>();
for (int i = ; i < ; i++)
{
priorityNodes.Add(new LinkedListNode<Document>(null));
}
} public void AddDocument(Document d)
{
Contract.Requires<ArgumentNullException>(d != null, "argument d must not be null");
// if (d == null) throw new ArgumentNullException("d"); AddDocumentToPriorityNode(d, d.Priority);
} private void AddDocumentToPriorityNode(Document doc, int priority)
{
Contract.Requires<ArgumentException>(priority >= && priority < , "priority value must be between 0 and 9");
//if (priority > 9 || priority < 0)
// throw new ArgumentException("Priority must be between 0 and 9"); if (priorityNodes[priority].Value == null)
{
--priority;
if (priority >= )
{
// check for the next lower priority
AddDocumentToPriorityNode(doc, priority);
}
else // now no priority node exists with the same priority or lower
// add the new document to the end
{
documentList.AddLast(doc);
priorityNodes[doc.Priority] = documentList.Last;
}
return;
}
else // a priority node exists
{
LinkedListNode<Document> prioNode = priorityNodes[priority];
if (priority == doc.Priority)
// priority node with the same priority exists
{
documentList.AddAfter(prioNode, doc); // set the priority node to the last document with the same priority
priorityNodes[doc.Priority] = prioNode.Next;
}
else // only priority node with a lower priority exists
{
// get the first node of the lower priority
LinkedListNode<Document> firstPrioNode = prioNode; while (firstPrioNode.Previous != null &&
firstPrioNode.Previous.Value.Priority == prioNode.Value.Priority)
{
firstPrioNode = prioNode.Previous;
prioNode = firstPrioNode;
} documentList.AddBefore(firstPrioNode, doc); // set the priority node to the new value
priorityNodes[doc.Priority] = firstPrioNode.Previous;
}
}
} public void DisplayAllNodes()
{
foreach (Document doc in documentList)
{
Console.WriteLine("priority: {0}, title {1}", doc.Priority, doc.Title);
}
} // returns the document with the highest priority
// (that's first in the linked list)
public Document GetDocument()
{
Document doc = documentList.First.Value;
documentList.RemoveFirst();
return doc;
} }
PriorityDocumentManager
class Program
{
static void Main()
{
PriorityDocumentManager pdm = new PriorityDocumentManager();
pdm.AddDocument(new Document("one", "Sample", ));
pdm.AddDocument(new Document("two", "Sample", ));
pdm.AddDocument(new Document("three", "Sample", ));
pdm.AddDocument(new Document("four", "Sample", ));
pdm.AddDocument(new Document("five", "Sample", ));
pdm.AddDocument(new Document("six", "Sample", ));
pdm.AddDocument(new Document("seven", "Sample", ));
pdm.AddDocument(new Document("eight", "Sample", )); pdm.DisplayAllNodes(); }
}
Program
6.有序列表
SortedList基于键对集合进行排序.
class Program
{
static void Main()
{
var books = new SortedList<string, string>();
books.Add("sty", "");
books.Add("abc", "");
books.Add("", "");
foreach (var item in books.Keys)
{
Console.WriteLine(item);
} }
}
abc
sty
7.字典
字典:用于在名称/值对中存储信息,字典的名称即键不能重复.
HashTable和Dictionary
1.HashTable大数据量插入数据时需要花费比Dictionary大的多的时间。
2.for方式遍历HashTable和Dictionary速度最快。
3.在foreach方式遍历时Dictionary遍历速度更快。
4.HashTable在取值时需要进行类型转换,Dictionary不用做类型转换。
在单线程的时候使用Dictionary更好一些,多线程的时候使用HashTable更好。
有序字典SortedList和SortedDictionary
SortedDictionary 泛型类是检索运算复杂度为 O(log n) 的二叉搜索树,其中 n 是字典中的元素数。就这一点而言,它与 SortedList 泛型类相似。这两个类具有相似的对象模型,并且都具有 O(log n) 的检索运算复杂度。这两个类的区别在于内存的使用以及插入和移除元素的速度:
SortedList 使用的内存比 SortedDictionary 少。
SortedDictionary 可对未排序的数据执行更快的插入和移除操作:它的时间复杂度为 O(log n),而SortedList 为 O(n)。
如果使用排序数据一次性填充列表,则 SortedList 比 SortedDictionary 快。
8.集
包含不重复元素的集合,叫“集”。.NET包含2个集。HashSet<T>和SortedSet<T>,它们继承ISet;SortedSet是一个有序集.
ISet提供了Add方法,如果HashSet中存在这个元素,再次使用Add方法不会抛出异常,返回bool值是否添加
var companyTeams = new HashSet<string>() { "Ferrari", "McLaren", "Mercedes" };
var traditionalTeams = new HashSet<string>() { "Ferrari", "McLaren" };
var privateTeams = new HashSet<string>() { "Red Bull", "Lotus", "Toro Rosso", "Force India", "Sauber" }; if (privateTeams.Add("Williams"))
Console.WriteLine("Williams added");
if (!companyTeams.Add("McLaren"))
Console.WriteLine("McLaren was already in this set");
IsSubsetOf方法判断了traditionalTeams集合是否companyTeams的子集
IsSupersetOf方法判断了companyTeams集合是否traditionalTeams的超集(包含它拥有的所有元素,并且多余它的元素)
var companyTeams = new HashSet<string>() { "Ferrari", "McLaren", "Mercedes" };
var traditionalTeams = new HashSet<string>() { "Ferrari", "McLaren" };
var privateTeams = new HashSet<string>() { "Red Bull", "Lotus", "Toro Rosso", "Force India", "Sauber" }; if (traditionalTeams.IsSubsetOf(companyTeams))
{
Console.WriteLine("traditionalTeams is subset of companyTeams");
} if (companyTeams.IsSupersetOf(traditionalTeams))
{
Console.WriteLine("companyTeams is a superset of traditionalTeams");
}
SortedSet的UnionWith方法可以修改这个集合,并且包含传入的集合
var allTeams = new SortedSet<string>(companyTeams);
allTeams.UnionWith(privateTeams);
allTeams.UnionWith(traditionalTeams);
9.可视察的集合
如果需要记录集合何时添加和删除元素的信息,可以使用ObservableCollection<T>,这个本身是为WPF定制的。
ObservableCollection<T>类用于创建自定义集合,在内部使用List<T>类,重写虚方法RemoveItem和SetItem()方法触发CollectionChanged事件。
class Program
{
static void Main()
{
var data = new ObservableCollection<string>();
data.CollectionChanged += Data_CollectionChanged;
data.Add("One");
data.Add("Two");
data.Insert(, "Three");
data.Remove("One"); } static void Data_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
Console.WriteLine("action: {0}", e.Action.ToString()); if (e.OldItems != null)
{
Console.WriteLine("starting index for old item(s): {0}", e.OldStartingIndex);
Console.WriteLine("old item(s):");
foreach (var item in e.OldItems)
{
Console.WriteLine(item);
}
}
if (e.NewItems != null)
{
Console.WriteLine("starting index for new item(s): {0}", e.NewStartingIndex);
Console.WriteLine("new item(s): ");
foreach (var item in e.NewItems)
{
Console.WriteLine(item);
}
} Console.WriteLine(); }
}
Data_CollectionChanged方法接收了NotifyCollectionChangedEventArgs,包含了集合的变化信息,Action属性给出了是否添加或删除一项的信息,对于删除的项,会设置OldItems属性,列出删除的项
对于添加的项,会设置NewItems属性,列出添加的项。
action: Add
starting index for new item(s):
new item(s):
One action: Add
starting index for new item(s):
new item(s):
Two action: Add
starting index for new item(s):
new item(s):
Three action: Remove
starting index for old item(s):
old item(s):
One
10.位数组
BitArray类的方法和属性
下表列出了一些BitArray类的常用属性:
属性 | 描述 |
---|---|
Count | 获取包含在BitArray元素的数量 |
IsReadOnly | 获取一个值,指示BitArray是否是只读 |
Item | 获取或设置在所述BitArray的特定位置的比特的值 |
Length | 获取或设置在BitArray元素的数量 |
下表列出了一些BitArray类的常用方法:
S.N | 方法名称及用途 |
---|---|
1 | public BitArray And( BitArray value ); 执行对指定BitArray的相应元素在当前BitArray元素的按位与运算 |
2 | public bool Get( int index ); 获取在所述BitArray的特定位置的比特的值 |
3 | public BitArray Not(); 反转当前BitArray所有的位值,使设置为true的元素被更改为false,并设置为false元素更改为true |
4 | public BitArray Or( BitArray value ); 在执行对指定BitArray的相应元素在当前BitArray的元素的按位或操作 |
5 | public void Set( int index, bool value ); 设置在所述BitArray为指定值的特定位置的比特值 |
6 | public void SetAll( bool value ); 设置在BitArray所有位设置为指定值 |
7 | public BitArray Xor( BitArray value ); 执行关于对在指定BitArray的相应元素中的当前BitArray的元素按位异或运算 |
当需要存储位,但不知道事先比特数就使用它。您可以通过使用一个整数索引,它从零开始访问BitArray集合中的项。
using System;
using System.Collections; namespace CollectionsApplication
{
class Program
{
static void Main(string[] args)
{
//creating two bit arrays of size 8
BitArray ba1 = new BitArray();
BitArray ba2 = new BitArray();
byte[] a = { };
byte[] b = { }; //storing the values 60, and 13 into the bit arrays
ba1 = new BitArray(a);
ba2 = new BitArray(b); //content of ba1
Console.WriteLine("Bit array ba1: 60");
for (int i = ; i < ba1.Count; i++)
{
Console.Write("{0, -6} ", ba1[i]);
}
Console.WriteLine(); //content of ba2
Console.WriteLine("Bit array ba2: 13");
for (int i = ; i < ba2.Count; i++)
{
Console.Write("{0, -6} ", ba2[i]);
}
Console.WriteLine(); BitArray ba3 = new BitArray();
ba3 = ba1.And(ba2); //content of ba3
Console.WriteLine("Bit array ba3 after AND operation: 12");
for (int i = ; i < ba3.Count; i++)
{
Console.Write("{0, -6} ", ba3[i]);
}
Console.WriteLine(); ba3 = ba1.Or(ba2);
//content of ba3
Console.WriteLine("Bit array ba3 after OR operation: 61");
for (int i = ; i < ba3.Count; i++)
{
Console.Write("{0, -6} ", ba3[i]);
}
Console.WriteLine(); Console.ReadKey();
}
}
}
让我们编译和运行上面的程序,这将产生以下结果:
Bit array ba1:
False False True True True True False False
Bit array ba2:
True False True True False False False False
Bit array ba3 after AND operation:
False False True True False False False False
Bit array ba3 after OR operation:
True False True True False False False False
BitVector32
提供了一个简单结构,该结构以32位内存存储布尔和小数值
对于内部使用的布尔值和小整数,BitVector32 比 BitArray 更有效。 BitArray 可以按需要无限地扩大,但它有内存和性能方面的系统开销,这是类实例所要求的。 相比之下,BitVector32 只使用 32 位。
BitVector32 结构可以设置成包含小整数的若干节或包含布尔值的若干位标志,但不能同时包含两者。BitVector32.Section 是 BitVector32 中的窗口,且由最小数量的连续位构成,连续位可以包含 CreateSection 中指定的最大值。 例如,带有最大值 1 的节只由一个位构成,而带有最大值 5 的节由三个位构成。 可以创建带有最大值 1 的 BitVector32.Section 作为布尔值,从而使您能够在同一 BitVector32 中存储整数和布尔值。
BitVector32 既可以设置为节,也可以设置为位标志,分别有成员可以应用于这两种情形。 例如,BitVector32.Item 属性是作为节设置的 BitVector32 的索引器,而 BitVector32.Item 属性是作为位标志设置的BitVector32 的索引器。 CreateMask 创建一系列屏蔽,这些屏蔽可用于访问作为位标志设置的 BitVector32 中的单个位。
在作为节设置的 BitVector32 上使用屏蔽可能会导致意外的结果。
using System;
using System.Collections.Specialized; public class SamplesBitVector32 { public static void Main() { // Creates and initializes a BitVector32 with all bit flags set to FALSE.
BitVector32 myBV = new BitVector32( ); // Creates masks to isolate each of the first five bit flags.
int myBit1 = BitVector32.CreateMask();
int myBit2 = BitVector32.CreateMask( myBit1 );
int myBit3 = BitVector32.CreateMask( myBit2 );
int myBit4 = BitVector32.CreateMask( myBit3 );
int myBit5 = BitVector32.CreateMask( myBit4 ); // Sets the alternating bits to TRUE.
Console.WriteLine( "Setting alternating bits to TRUE:" );
Console.WriteLine( " Initial: {0}", myBV.ToString() );
myBV[myBit1] = true;
Console.WriteLine( " myBit1 = TRUE: {0}", myBV.ToString() );
myBV[myBit3] = true;
Console.WriteLine( " myBit3 = TRUE: {0}", myBV.ToString() );
myBV[myBit5] = true;
Console.WriteLine( " myBit5 = TRUE: {0}", myBV.ToString() ); } } /*
This code produces the following output. Setting alternating bits to TRUE:
Initial: BitVector32{00000000000000000000000000000000}
myBit1 = TRUE: BitVector32{00000000000000000000000000000001}
myBit3 = TRUE: BitVector32{00000000000000000000000000000101}
myBit5 = TRUE: BitVector32{00000000000000000000000000010101} */
BitVector用作节集合 using System;
using System.Collections.Specialized; public class SamplesBitVector32 { public static void Main() { // Creates and initializes a BitVector32.
BitVector32 myBV = new BitVector32( ); // Creates four sections in the BitVector32 with maximum values 6, 3, 1, and 15.
// mySect3, which uses exactly one bit, can also be used as a bit flag.
BitVector32.Section mySect1 = BitVector32.CreateSection( );
BitVector32.Section mySect2 = BitVector32.CreateSection( , mySect1 );
BitVector32.Section mySect3 = BitVector32.CreateSection( , mySect2 );
BitVector32.Section mySect4 = BitVector32.CreateSection( , mySect3 ); // Displays the values of the sections.
Console.WriteLine( "Initial values:" );
Console.WriteLine( "\tmySect1: {0}", myBV[mySect1] );
Console.WriteLine( "\tmySect2: {0}", myBV[mySect2] );
Console.WriteLine( "\tmySect3: {0}", myBV[mySect3] );
Console.WriteLine( "\tmySect4: {0}", myBV[mySect4] ); // Sets each section to a new value and displays the value of the BitVector32 at each step.
Console.WriteLine( "Changing the values of each section:" );
Console.WriteLine( "\tInitial: \t{0}", myBV.ToString() );
myBV[mySect1] = ;
Console.WriteLine( "\tmySect1 = 5:\t{0}", myBV.ToString() );
myBV[mySect2] = ;
Console.WriteLine( "\tmySect2 = 3:\t{0}", myBV.ToString() );
myBV[mySect3] = ;
Console.WriteLine( "\tmySect3 = 1:\t{0}", myBV.ToString() );
myBV[mySect4] = ;
Console.WriteLine( "\tmySect4 = 9:\t{0}", myBV.ToString() ); // Displays the values of the sections.
Console.WriteLine( "New values:" );
Console.WriteLine( "\tmySect1: {0}", myBV[mySect1] );
Console.WriteLine( "\tmySect2: {0}", myBV[mySect2] );
Console.WriteLine( "\tmySect3: {0}", myBV[mySect3] );
Console.WriteLine( "\tmySect4: {0}", myBV[mySect4] ); } }
/*
This code produces the following output. Initial values:
mySect1: 0
mySect2: 0
mySect3: 0
mySect4: 0
Changing the values of each section:
Initial: BitVector32{00000000000000000000000000000000}
mySect1 = 5: BitVector32{00000000000000000000000000000101}
mySect2 = 3: BitVector32{00000000000000000000000000011101}
mySect3 = 1: BitVector32{00000000000000000000000000111101}
mySect4 = 9: BitVector32{00000000000000000000001001111101}
New values:
mySect1: 5
mySect2: 3
mySect3: 1
mySect4: 9 */
11.不变的集合
Net提供的不可变集合
ImmutableStack<int> a1 = ImmutableStack<int>.Empty;
ImmutableStack<int> a2 = a1.Push();
ImmutableStack<int> a3 = a2.Push();
ImmutableStack<int> a4 = a3.Push();
ImmutableStack<int> iv3 = a4.Pop();
使用Net不可变列表集合有一点要注意的是,当我们Push值时要重新赋值给原变量才正确,因为push后会生成一个新对象,原a1只是旧值:
ImmutableStack<int> a1 = ImmutableStack<int>.Empty;
a1.Push(); //不正确,a1仍是空值值,push会生成新的栈。
a1 = a1.Push(); //需要将新栈重新赋值给a1
NET提供的常用数据结构
1.ImmutableStack
2.ImmutableQueue
3.ImmutableList
4.ImmutableHashSet
5.ImmutableSortedSet
6.ImmutableDictionary<K, V>
7.ImmutableSortedDictionary<K, V>
不可变优点
1.集合共享安全,从不被改变
2.访问集合时,不需要锁集合(线程安全)
3.修改集合不担心旧集合被改变
4.书写更简洁,函数式风格。 var list = ImmutableList.Empty.Add(10).Add(20).Add(30);
5.保证数据完整性,安全性
不可变对象缺点
不可变本身的优点即是缺点,当每次对象/集合操作都会返回个新值。而旧值依旧会保留一段时间,这会使内存有极大开销,也会给GC造成回收负担,性能也比可变集合差的多。
12.并发集合
线程安全的集合可防止多个线程以相互冲突的方式访问集合
.NET 的System.Collections.Concurrent提供了几个安全的类和功能:
类 | 说明 | |
---|---|---|
BlockingCollection<T> |
为实现 IProducerConsumerCollection<T> 的线程安全集合提供阻塞和限制功能。 |
|
ConcurrentBag<T> |
表示对象的线程安全的无序集合。 |
|
ConcurrentDictionary<TKey, TValue> |
表示可由多个线程同时访问的键/值对的线程安全集合。 |
|
ConcurrentQueue<T> |
表示线程安全的先进先出 (FIFO) 集合。 |
|
ConcurrentStack<T> |
表示线程安全的后进先出 (LIFO) 集合。 |
|
OrderablePartitioner<TSource> |
表示将可排序数据源拆分为多个分区的特定方式。 |
|
Partitioner |
为数组、列表和可枚举对象提供常见的分区策略。 |
|
Partitioner<TSource> |
表示将数据源拆分为多个分区的特定方式。 |
1)创建管道
将这些并发集合类用于管道,一个任务向一个集合类写入一些内容,同时另一个任务从该集合中读取内容
示例中多个任务形成一个管道.
第一个管道,
第1阶段的任务读取文件名,添加到队列,这个任务运行同时,
第2阶段的任务已经开始从队列中读取文件名并加载它们的程序,结果被写入另一个队列。
第3阶段同时启动,读取并处理第2个队列的内容,结果被写入一个字典。
第3阶段完成,并且内容已被最终处理,字典得到完整结果时,下一阶段才开始。
第4阶段从字典中读取内容,转换数据,然后写入队列中
第5阶段在项中添加颜色信息,然后把它们添加到另一个队列中,最后一个阶段显示信息。
第4到第6阶段也可以并发运行.
class Program
{
static void Main(string[] args)
{
StartPipeline();
Console.ReadLine();
} private static async void StartPipeline()
{
var fileNames = new BlockingCollection<string>();
var lines = new BlockingCollection<string>();
var words = new ConcurrentDictionary<string, int>();
var items = new BlockingCollection<Info>();
var coloredItems = new BlockingCollection<Info>(); Task t1 = PipelineStages.ReadFilenamesAsync(@"../../..", fileNames);
ConsoleHelper.WriteLine("started stage 1");
Task t2 = PipelineStages.LoadContentAsync(fileNames, lines);
ConsoleHelper.WriteLine("started stage 2");
Task t3 = PipelineStages.ProcessContentAsync(lines, words);
await Task.WhenAll(t1, t2, t3);
ConsoleHelper.WriteLine("stages 1, 2, 3 completed"); Task t4 = PipelineStages.TransferContentAsync(words, items);
Task t5 = PipelineStages.AddColorAsync(items, coloredItems);
Task t6 = PipelineStages.ShowContentAsync(coloredItems);
ConsoleHelper.WriteLine("stages 4, 5, 6 started"); await Task.WhenAll(t4, t5, t6); ConsoleHelper.WriteLine("all stages finished");
}
}
Program
public class ConsoleHelper
{
private static object syncOutput = new object(); public static void WriteLine(string message)
{
lock (syncOutput)
{
Console.WriteLine(message);
}
} public static void WriteLine(string message, string color)
{
lock (syncOutput)
{
Console.ForegroundColor = (ConsoleColor)Enum.Parse(typeof(ConsoleColor), color);
Console.WriteLine(message);
Console.ResetColor();
}
}
}
ConsoleHelper
public static class PipelineStages
{
public static Task ReadFilenamesAsync(string path, BlockingCollection<string> output)
{
return Task.Run(() =>
{
foreach (string filename in Directory.EnumerateFiles(path, "*.cs", SearchOption.AllDirectories))
{
output.Add(filename);
ConsoleHelper.WriteLine(string.Format("stage 1: added {0}", filename));
}
output.CompleteAdding();
});
} public static async Task LoadContentAsync(BlockingCollection<string> input, BlockingCollection<string> output)
{
foreach (var filename in input.GetConsumingEnumerable())
{
using (FileStream stream = File.OpenRead(filename))
{
var reader = new StreamReader(stream);
string line = null;
while ((line = await reader.ReadLineAsync()) != null)
{
output.Add(line);
ConsoleHelper.WriteLine(string.Format("stage 2: added {0}", line));
}
}
}
output.CompleteAdding();
} public static Task ProcessContentAsync(BlockingCollection<string> input, ConcurrentDictionary<string, int> output)
{
return Task.Run(() =>
{
foreach (var line in input.GetConsumingEnumerable())
{
string[] words = line.Split(' ', ';', '\t', '{', '}', '(', ')', ':', ',', '"');
foreach (var word in words.Where(w => !string.IsNullOrEmpty(w)))
{
output.AddOrIncrementValue(word);
ConsoleHelper.WriteLine(string.Format("stage 3: added {0}", word));
}
}
});
} public static Task TransferContentAsync(ConcurrentDictionary<string, int> input, BlockingCollection<Info> output)
{
return Task.Run(() =>
{
foreach (var word in input.Keys)
{
int value;
if (input.TryGetValue(word, out value))
{
var info = new Info { Word = word, Count = value };
output.Add(info);
ConsoleHelper.WriteLine(string.Format("stage 4: added {0}", info));
}
}
output.CompleteAdding();
});
} public static Task AddColorAsync(BlockingCollection<Info> input, BlockingCollection<Info> output)
{
return Task.Run(() =>
{
foreach (var item in input.GetConsumingEnumerable())
{
if (item.Count > )
{
item.Color = "Red";
}
else if (item.Count > )
{
item.Color = "Yellow";
}
else
{
item.Color = "Green";
}
output.Add(item);
ConsoleHelper.WriteLine(string.Format("stage 5: added color {1} to {0}", item, item.Color));
}
output.CompleteAdding();
});
} public static Task ShowContentAsync(BlockingCollection<Info> input)
{
return Task.Run(() =>
{
foreach (var item in input.GetConsumingEnumerable())
{
ConsoleHelper.WriteLine(string.Format("stage 6: {0}", item), item.Color);
}
});
}
}
PipelineStages
第1阶段的ReadFilenamesAsync方法,实现了迭代目录文件名。在完成文件名添加后调用output.CompleteAdding();用以通知所有读取器不再等待集合中任何额外的项.如果没有调用的话,循环中读取器会添加等待更多的项.
public static Task ReadFilenamesAsync(string path, BlockingCollection<string> output)
{
return Task.Run(() =>
{
foreach (string filename in Directory.EnumerateFiles(path, "*.cs", SearchOption.AllDirectories))
{
output.Add(filename);
ConsoleHelper.WriteLine(string.Format("stage 1: added {0}", filename));
}
output.CompleteAdding();
});
}
ReadFilenamesAsync
下一阶段读取文件并将器内容添加到另一个集合中,由LoadContentAsync方法完成,该方法使用了输入集合传递的文件名,打开文件,把文件中的所有行添加到输出的集合中。在循环中用输入阻塞集合调用GetConsumingEnumerable()方法,以迭代各项,不使用也是可以的,但是值会迭代当前状态的集合。不会迭代以后添加的项。
如果在填充集合的同时,使用读取器读取集合,则需要使用GetConsumingEnumerable()方法获取阻塞集合的枚举器,而不是直接迭代集合
public static async Task LoadContentAsync(BlockingCollection<string> input, BlockingCollection<string> output)
{
foreach (var filename in input.GetConsumingEnumerable())
{
using (FileStream stream = File.OpenRead(filename))
{
var reader = new StreamReader(stream);
string line = null;
while ((line = await reader.ReadLineAsync()) != null)
{
output.Add(line);
ConsoleHelper.WriteLine(string.Format("stage 2: added {0}", line));
}
}
}
output.CompleteAdding();
}
LoadContentAsync
public static Task ProcessContentAsync(BlockingCollection<string> input, ConcurrentDictionary<string, int> output)
{
return Task.Run(() =>
{
foreach (var line in input.GetConsumingEnumerable())
{
string[] words = line.Split(' ', ';', '\t', '{', '}', '(', ')', ':', ',', '"');
foreach (var word in words.Where(w => !string.IsNullOrEmpty(w)))
{
output.AddOrIncrementValue(word);
ConsoleHelper.WriteLine(string.Format("stage 3: added {0}", word));
}
}
});
}
ProcessContentAsync
public static class ConcurrentDictionaryExtension
{
public static void AddOrIncrementValue(this ConcurrentDictionary<string, int> dict, string key)
{
bool success = false;
while (!success)
{
int value;
if (dict.TryGetValue(key, out value))
{
if (dict.TryUpdate(key, value + , value))
{
success = true;
}
}
else
{
if (dict.TryAdd(key, ))
{
success = true;
}
}
}
}
}
ConcurrentDictionaryExtension
在完成第3个阶段后,第4到6阶段也可以并行运行,TransferContentAsync从字典中获取数据,进行类型转换,输出到BlockingCollection<string>中
public static Task ProcessContentAsync(BlockingCollection<string> input, ConcurrentDictionary<string, int> output)
{
return Task.Run(() =>
{
foreach (var line in input.GetConsumingEnumerable())
{
string[] words = line.Split(' ', ';', '\t', '{', '}', '(', ')', ':', ',', '"');
foreach (var word in words.Where(w => !string.IsNullOrEmpty(w)))
{
output.AddOrIncrementValue(word);
ConsoleHelper.WriteLine(string.Format("stage 3: added {0}", word));
}
}
});
} public static Task TransferContentAsync(ConcurrentDictionary<string, int> input, BlockingCollection<Info> output)
{
return Task.Run(() =>
{
foreach (var word in input.Keys)
{
int value;
if (input.TryGetValue(word, out value))
{
var info = new Info { Word = word, Count = value };
output.Add(info);
ConsoleHelper.WriteLine(string.Format("stage 4: added {0}", info));
}
}
output.CompleteAdding();
});
} public static Task AddColorAsync(BlockingCollection<Info> input, BlockingCollection<Info> output)
{
return Task.Run(() =>
{
foreach (var item in input.GetConsumingEnumerable())
{
if (item.Count > )
{
item.Color = "Red";
}
else if (item.Count > )
{
item.Color = "Yellow";
}
else
{
item.Color = "Green";
}
output.Add(item);
ConsoleHelper.WriteLine(string.Format("stage 5: added color {1} to {0}", item, item.Color));
}
output.CompleteAdding();
});
} public static Task ShowContentAsync(BlockingCollection<Info> input)
{
return Task.Run(() =>
{
foreach (var item in input.GetConsumingEnumerable())
{
ConsoleHelper.WriteLine(string.Format("stage 6: {0}", item), item.Color);
}
});
}
13.性能
集合的方法常常有性能提示,给出大写O记录操作时间。
O(1)表示无论集合中有多少数据项,这个操作需要的时间都不变。
O(n)表示对于集合执行一个操作需要的事件在最坏情况时是N.
O(log n)表示操作需要的时间随集合中元素的增加而增加
非泛型类集合
泛型集合类是在.NET2.0的时候出来的,也就是说在1.0的时候是没有这么方便的东西的。现在基本上我们已经不使用这些集合类了,除非在做一些和老代码保持兼容的工作的时候。来看看1.0时代的.NET程序员们都有哪些集合类可以用。
ArraryList后来被List<T>替代。
HashTable 后来被Dictionary<TKey,TValue>替代。
Queue 后来被Queue<T>替代。
SortedList 后来被SortedList<T>替代。
Stack 后来被Stack<T>替代。
线程安全的集合类
ConcurrentQueue 线程安全版本的Queue
ConcurrentStack线程安全版本的Stack
ConcurrentBag线程安全的对象集合
ConcurrentDictionary线程安全的Dictionary
BlockingCollection
C#高级编程9-第10章 集合的更多相关文章
- 读《C#高级编程》第1章问题
读<C#高级编程>第1章 .Net机构体系笔记 网红的话:爸爸说我将来会是一个牛逼的程序员,因为我有一个梦,虽然脑壳笨但是做事情很能坚持. 本章主要是了解.Net的结构,都是一些概念,并没 ...
- C#高级编程9 第17章 使用VS2013-C#特性
C#高级编程9 第17章 使用VS2013 编辑定位到 如果默认勾选了这项,请去掉勾选,因为勾选之后解决方案的目录会根据当前文件选中. 可以设置项目并行生成数 版本控制软件设置 所有文本编辑器行号显示 ...
- C#高级编程9 第18章 部署
C#高级编程9 第18章 部署 使用 XCopy 进行部署 本主题演示如何通过将应用程序文件从一台计算机复制到另一台计算机来部署应用程序. 1.将项目中生成的程序集复制到目标计算机,生成的程序集位于项 ...
- C#高级编程9 第16章 错误和异常
C#高级编程9 第16章 错误和异常 了解这章可以学会如何处理系统异常以及错误信息. System.Exception类是.NET运行库抛出的异常,可以继承它定义自己的异常类. try块代码包含的代码 ...
- C#高级编程笔记之第二章:核心C#
变量的初始化和作用域 C#的预定义数据类型 流控制 枚举 名称空间 预处理命令 C#编程的推荐规则和约定 变量的初始化和作用域 初始化 C#有两个方法可以一确保变量在使用前进行了初始化: 变量是字段, ...
- Windows核心编程:第10章 同步设备IO与异步设备IO
Github https://github.com/gongluck/Windows-Core-Program.git //第10章 同步设备IO与异步设备IO.cpp: 定义应用程序的入口点. // ...
- C#高级编程9 第14章 内存管理和指针
C#高级编程9 内存管理和指针 后台内存管理 1) 值数据类型 在处理器的虚拟内存中有一个区域,称为栈,栈存储变量的浅副本数据,通过进入变量的作用域划分区域,通过离开变量的作用域释放. 栈的指针指向栈 ...
- C#高级编程(第9版) 第10章 集合 笔记
话说 虽然敲过好多代码, 但除了C++,一直没正眼瞧过其它语言.(没办法 谁叫C++既有oop又能自由控制内存呢) 今天 看公司老项目的src,c#的,linq+Dictionary的用法有感.所以找 ...
- Java使用实现面向对象编程:第七章集合框架的解读=>重中之重
对于集合框架,是非常重要的知识,是程序员必须要知道的知识点. 但是我们为什么要引入集合框架呢? 我们之前用过数组存储数据,但是采用数组存储存在了很多的缺陷.而现在我们引用了集合框架,可以完全弥补了数组 ...
- python高级编程(第12章:优化学习)1
# -*- coding: utf-8 -*-# python:2.x__author__ = 'Administrator'#由于5,6,7,8,9,10,11主要是在包,测试之类的学习所以这边就不 ...
随机推荐
- 一步一步搭建 oracle 11gR2 rac+dg之grid安装(四)【转】
一步一步在RHEL6.5+VMware Workstation 10上搭建 oracle 11gR2 rac + dg 之grid安装 (四) 转自 一步一步搭建 oracle 11gR2 rac+d ...
- python小工具之读取host文件
# -*- coding: utf-8 -*- # @Time : 2018/9/12 21:09 # @Author : cxa # @File : readhostfile.py # @Softw ...
- Go 的package
一.包的一些基本的概念 1.在同一个目录下的所有go文件中,只能有一个main函数.如果存在多个main函数,则在编译的时候会报错 那么,在同一个目录下的两个go文件究竟是什么关系? 为什么会彼此影响 ...
- IOS使用SourceTree
一.安装sourceTree 1.下载 访问SourceTree 软件官方下载地址 : https://www.sourcetreeapp.com 下载macos版本 2.安装 安装和windows安 ...
- 洛谷P1455搭配购买
传送门啦 这是强连通分量与背包的例题 需要注意的就是价值和价格两个数组不要打反了.. 另外 这是双向图!!! #include <iostream> #include <cstdio ...
- numpy数学计算
1.求范数 np.linalg.norm norm(x, ord=None, axis=None, keepdims=False) 范数理论的一个小推论告诉我们:ℓ1≥ℓ2≥ℓ∞
- Effective STL 学习笔记 Item 16:vector, string & C API
有时需要支持 C 的接口,但这并不复杂. 对于 vector 来讲, \(v[0]\) 的地址 \(\&v[0]\) 即可作为数组指针传递给 C API: 1: // Legacy C API ...
- 减小VirtualBox虚拟硬盘文件的大小
虚拟机使用久了就会发现虚拟硬盘越来越大,但是进入虚拟机里的系统用命令看了下,实际占用的空间远没有虚拟硬盘大小那么大,这个让人很不爽,而且在分享虚拟机镜像的时候也很不方便.VirtualBox似乎没有提 ...
- drools7 (一、最简单的例子)
切记!!! 必须使用jdk1.8 工程目录 引入依赖包,pom.xml <?xml version="1.0" encoding="UTF-8"?> ...
- 【POJ】2449.Remmarguts' Date(K短路 n log n + k log k + m算法,非A*,论文算法)
题解 (搬运一个原来博客的论文题) 抱着板题的心情去,结果有大坑 就是S == T的时候也一定要走,++K 我发现按照论文写得\(O(n \log n + m + k \ log k)\)算法没有玄学 ...