C# 常用集合
ArrayList
根据大小动态添加的数据
-
ArrayList不保证进行排序。 在执行 (操作(例如BinarySearch需要 ArrayList 排序的) )之前,必须ArrayList调用其 Sort 方法对 进行排序。 若要维护在添加新元素时自动排序的集合,可以使用 SortedSet
类。 -
将元素添加到 时 ArrayList,容量会根据需要通过重新分配自动增加。 可以通过调用 TrimToSize 或 显式设置 属性来 Capacity 减小容量。
-
仅对于.NET Framework:对于非常大ArrayList的对象,可以通过在运行时环境中将配置元素的
属性设置为 enabled ,将 64 位系统上的最大容量增加到 true 20 亿个元素。
可以使用整数索引访问此集合中的元素。 此集合中的索引从零开始。 -
集合 ArrayList 接受 null 作为有效值。 它还允许重复元素。
-
不支持将多维数组用作集合中的 ArrayList 元素。
-
ArrayList会进行装修和拆箱操作
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace SetTest
{
internal class ArrayListTest : ISet
{
public void Show()
{
ArrayList myAL = new ArrayList();
myAL.Add("Hello");
myAL.Add("World");
myAL.Add("!");
Console.WriteLine($"个数:{myAL.Count}");
Console.WriteLine($"Capacity:{myAL.Capacity}");
Console.Write("值为:");
foreach (Object obj in myAL)
{
Console.Write(obj + " ");
}
Console.WriteLine();
}
}
}
微软建议:不建议使用 ArrayList 类进行新的开发。 建议改用泛型 List
类
List
可通过索引访问的对象的强类型集合
-
List
等效于ArrayList,它实现了IList 。 -
它在System.Collection.Generic命名空间下。
-
List
可以包含指定类型的元素。它提供编译时类型检查,并且不执行装箱/拆箱,因为它是泛型的。 -
可以使用Add(),AddRange()方法或collection-initializer(集合初始化器)语法添加元素。
-
可以通过传递索引来访问元素,例如myList[0]。索引从零开始。
-
List
与ArrayList相比,执行速度更快,出错更少。 -
List
接受 null 作为引用类型的有效值,并允许重复元素
public class Part : IEquatable<Part>
{
public string PartName { get; set; }
public int PartId { get; set; }
public override string ToString()
{
return "ID: " + PartId + " Name: " + PartName;
}
public override bool Equals(object obj)
{
if (obj == null) return false;
Part objAsPart = obj as Part;
if (objAsPart == null) return false;
else return Equals(objAsPart);
}
public override int GetHashCode()
{
return PartId;
}
public bool Equals(Part other)
{
if (other == null) return false;
return (this.PartId.Equals(other.PartId));
}
}
public class ListTest : ISet
{
public void Show()
{
List<Part> parts = new List<Part>();
parts.Add(new Part() { PartName = "Test1", PartId = 1234 });
parts.Add(new Part() { PartName = "Test2", PartId = 1334 });
parts.Add(new Part() { PartName = "Test3", PartId = 1434 });
parts.Add(new Part() { PartName = "Test4", PartId = 1444 });
parts.Add(new Part() { PartName = "Test5", PartId = 1534 });
parts.Add(new Part() { PartName = "Test6", PartId = 1634 });
Console.WriteLine();
foreach (Part aPart in parts)
{
Console.WriteLine(aPart);
}
parts.Contains(new Part { PartId = 1734, PartName = "" });
Console.WriteLine("\nInsert(2, \"1834\")");
parts.Insert(2, new Part() { PartName = "Test Insert", PartId = 1834 });
foreach (Part aPart in parts)
{
Console.WriteLine(aPart);
}
Console.WriteLine("\nParts[3]: {0}", parts[3]);
Console.WriteLine("\nRemove(\"1534\")");
parts.Remove(new Part() { PartId = 1534, PartName = "Test5" });
Console.WriteLine();
foreach (Part aPart in parts)
{
Console.WriteLine(aPart);
}
Console.WriteLine("\nRemoveAt(3)");
parts.RemoveAt(3);
Console.WriteLine();
foreach (Part aPart in parts)
{
Console.WriteLine(aPart);
}
}
}
以上的结果为:
Queue
表示先进先出的集合
-
Enqueue将元素添加到 Queue
-
Dequeue 从 Queue开头删除开头的元素。
-
Peek 返回开头的元素,该元素位于开头 Queue ,但不删除该元素
-
Queue 接受 null 为有效值,并允许重复元素。
-
没有实现 IList,ICollection。所以它不能按索引访问元素,不能使用Add和Remove
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace SetTest
{
public class QueueTest : ISet
{
public void Show()
{
Queue myQ = new Queue();
myQ.Enqueue("Hello");
myQ.Enqueue("World");
myQ.Enqueue("!");
Console.WriteLine("数量为:{0}", myQ.Count);
Console.Write("值:");
foreach (Object obj in myQ)
{
Console.Write(" {0}", obj);
}
Console.WriteLine();
}
}
}
以上的执行结果为:
Stack
后进先出的集合
-
Push将元素添加到 Stack
-
Pop 从 Stack开头删除开头的元素。
-
Queue 接受 null 为有效值,并允许重复元素。
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace SetTest
{
public class StackTest : ISet
{
public void Show()
{
Stack myStack = new Stack();
myStack.Push("Hello");
myStack.Push("World");
myStack.Push("!");
Console.WriteLine("数量:{0}", myStack.Count);
Console.Write("值:");
foreach (Object obj in myStack)
{
Console.Write(" {0}", obj);
}
Console.WriteLine();
}
}
}
以上的执行的结果为:
LinkedList
双重链表
-
LinkedList
是通用链表。它支持枚举器。 -
插入和移除是O(1)操作。
-
你可以删除节点并将它们重新插入同一列表或另一个列表中, 这将导致在堆上没有分配其他对象。
-
由于列表还维护内部计数, 因此获取Count属性是O(1)操作。
-
LinkedList中的每个节点T>对象的类型为LinkedListNode
。 -
LinkedList类不支持链接, 拆分, 循环或其他可能会使列表处于不一致状态的功能。
-
如果LinkedList为空, 则第一和持续属性包含null。
-
LinkedList是双重链接的, 因此, 每个节点都指向下一个节点, 并指向上一个节点
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace SetTest
{
public class LinkedListTest : ISet
{
public void Show()
{
string[] words = { "the", "fox", "jumps", "over", "the", "dog" };
LinkedList<string> sentence = new LinkedList<string>(words);
Display(sentence, "链表值为:");
Console.WriteLine("链表是否包含值(\"jumps\") = {0}", sentence.Contains("jumps"));
sentence.AddFirst("today");
Display(sentence, "测试 1: 新增值:'today'到链表开头:");
LinkedListNode<string> mark1 = sentence.First;
sentence.RemoveFirst();
sentence.AddLast(mark1);
Display(sentence, "测试 2: 将第一个节点移动到最后一个节点:");
sentence.RemoveLast();
sentence.AddLast("yesterday");
Display(sentence, "测试 3:修改最后一个节点的值为: 'yesterday':");
mark1 = sentence.Last;
sentence.RemoveLast();
sentence.AddFirst(mark1);
Display(sentence, "测试 4: 将最后一个节点移动到第一个节点:");
sentence.RemoveFirst();
LinkedListNode<string> current = sentence.FindLast("the");
IndicateNode(current, "测试 5: 找到最后一个值为: 'the' 的节点:");
sentence.AddAfter(current, "old");
sentence.AddAfter(current, "lazy");
IndicateNode(current, "测试 6: 将值: 'lazy' 和 'old' 添加到 'the' 之后:");
current = sentence.Find("fox");
IndicateNode(current, "测试 7: 找到值为: 'fox' 的节点:");
sentence.AddBefore(current, "quick");
sentence.AddBefore(current, "brown");
IndicateNode(current, "测试 8: 将值为: 'quick' 和 'brown' 添加到值: 'fox' 之前:");
mark1 = current;
LinkedListNode<string> mark2 = current.Previous;
current = sentence.Find("dog");
IndicateNode(current, "测试 9: 找到值为: 'dog' 的节点:");
Console.WriteLine("测试 10: 抛出一个异常: 添加一个已经存在的节点值 'fox' :");
try
{
sentence.AddBefore(current, mark1);
}
catch (InvalidOperationException ex)
{
Console.WriteLine("错误信息为: {0}", ex.Message);
}
Console.WriteLine();
sentence.Remove(mark1);
sentence.AddBefore(current, mark1);
IndicateNode(current, "测试 11: 在节点'fox'前添加节点 'dog':");
sentence.Remove(current);
IndicateNode(current, "测试 12: 删除节点 'dog' and attempt to indicate it:");
sentence.AddAfter(mark2, current);
IndicateNode(current, "测试 13: 在节点'' 后添加新节点 'brown':");
sentence.Remove("old");
Display(sentence, "测试 14:删除 'old' 节点值:");
sentence.RemoveLast();
ICollection<string> icoll = sentence;
icoll.Add("rhinoceros");
Display(sentence, "测试 15: 删除最后一个节点, 将链表转化成 ICollection, 然后添加值: 'rhinoceros':");
Console.WriteLine("测试 16: 拷贝当前链表至数组:");
string[] sArray = new string[sentence.Count];
sentence.CopyTo(sArray, 0);
foreach (string s in sArray)
{
Console.WriteLine(s);
}
//清空链表
sentence.Clear();
Console.WriteLine();
Console.WriteLine("测试 17: Clear linked list. Contains 'jumps' = {0}", sentence.Contains("jumps"));
Console.ReadLine();
}
private static void Display(LinkedList<string> words, string test)
{
Console.WriteLine(test);
foreach (string word in words)
{
Console.Write(word + " ");
}
Console.WriteLine();
Console.WriteLine();
}
private static void IndicateNode(LinkedListNode<string> node, string test)
{
Console.WriteLine(test);
if (node.List == null)
{
Console.WriteLine("Node '{0}' is not in the list.\n",
node.Value);
return;
}
StringBuilder result = new StringBuilder("(" + node.Value + ")");
LinkedListNode<string> nodeP = node.Previous;
while (nodeP != null)
{
result.Insert(0, nodeP.Value + " ");
nodeP = nodeP.Previous;
}
node = node.Next;
while (node != null)
{
result.Append(" " + node.Value);
node = node.Next;
}
Console.WriteLine(result);
Console.WriteLine();
}
}
}
以上的输出结果为:
SortedList
有序键值对集合
-
SortedList对象在内部维护两个数组来存储列表的元素;即,一个数组用于键Key,另一个数组用于关联值Value。 每个元素都是可作为 对象访问 DictionaryEntry 的键/值对。
-
对象的容量 SortedList 是 可以容纳的元素 SortedList 数。 将元素添加到 时 SortedList,容量会根据需要通过重新分配自动增加。 可以通过调用 TrimToSize 或 显式设置 属性来 Capacity 减小容量。
-
仅.NET Framework:对于非常大SortedList的对象,可以通过在运行时环境中将配置元素的 gcAllowVeryLargeObjects 属性设置为 enabled ,将 64 位系统上的最大容量增加到 20 亿个元素。
-
对象的元素SortedList根据创建 时SortedList指定的特定IComparer实现或IComparable键本身提供的实现按键排序。
-
在任一 SortedList 情况下, 都不允许重复键。
-
索引序列基于排序序列。 添加元素时,它将按正确的排序顺序插入到 中 SortedList ,索引会相应地调整。 删除元素时,索引也会相应地进行调整。 因此,在对象中添加或移除元素时,特定键/值对的 SortedList 索引可能会更改。
-
由于排序, SortedList 对对象的操作往往比对 Hashtable 对象的操作慢。 但是, SortedList 允许通过关联的键或索引访问值,从而提供了更大的灵活性。
-
可以使用整数索引访问此集合中的元素。 此集合中的索引从零开始。
-
键不能是 null,但值可以是 。
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace SetTest
{
public class SortedListTest : ISet
{
public void Show()
{
SortedList mySL = new SortedList();
mySL.Add("Third", "!");
mySL.Add("Second", "World");
mySL.Add("First", "Hello");
Console.WriteLine("个数:{0}", mySL.Count);
Console.WriteLine("容量:{0}", mySL.Capacity);
Console.WriteLine("键和值:");
Console.WriteLine("-键-\t-值-");
for (int i = 0; i < mySL.Count; i++)
{
Console.WriteLine("{0}:\t{1}", mySL.GetKey(i), mySL.GetByIndex(i));
}
Console.WriteLine();
}
}
}
以上测试的输出结果:
Dictionary
键值对集合
-
属于System.Collection.Generic命名空间。
-
实现 IDictionary <TKey,TValue>接口。
-
键必须是唯一的,不能为null。值可以为null或重复。
-
Dictionary里面的每一个元素都是一个键值对(由二个元素组成:键和值)
-
键必须是唯一的,而值不需要唯一的
-
键和值都可以是任何类型(比如:string, int, 自定义类型等等)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace SetTest
{
internal class DictionaryTest : ISet
{
public void Show()
{
Dictionary<string, string> openWith =
new Dictionary<string, string>();
openWith.Add("txt", "notepad.exe");
openWith.Add("bmp", "paint.exe");
openWith.Add("dib", "paint.exe");
openWith.Add("rtf", "wordpad.exe");
try
{
openWith.Add("txt", "winword.exe");
}
catch (ArgumentException)
{
Console.WriteLine("Key为\"txt\"已经存在");
}
Console.WriteLine("key为\"rtf\", 字典内值为{0}",
openWith["rtf"]);
openWith["rtf"] = "winword.exe";
Console.WriteLine("key为\"rtf\", 字典内值为{0}",
openWith["rtf"]);
openWith["doc"] = "winword.exe";
try
{
Console.WriteLine("key为\"tif\", 字典内值为{0}",
openWith["tif"]);
}
catch (KeyNotFoundException)
{
Console.WriteLine("Key为\"tif\"的字典值不存在");
}
string value = "";
if (openWith.TryGetValue("tif", out value))
{
Console.WriteLine("key为\"tif\", 字典内值为{0}.", value);
}
else
{
Console.WriteLine("Key为\"tif\"的字典值不存在.");
}
if (!openWith.ContainsKey("ht"))
{
openWith.Add("ht", "hypertrm.exe");
Console.WriteLine("值新增到 key 为 \"ht\"中: {0}",
openWith["ht"]);
}
Console.WriteLine("");
Console.WriteLine("打印字典如下");
foreach (KeyValuePair<string, string> kvp in openWith)
{
Console.WriteLine("Key = {0}, Value = {1}",
kvp.Key, kvp.Value);
}
Dictionary<string, string>.ValueCollection valueColl =
openWith.Values;
Console.WriteLine();
Console.WriteLine("打印值如下");
foreach (string s in valueColl)
{
Console.WriteLine("Value = {0}", s);
}
Dictionary<string, string>.KeyCollection keyColl =
openWith.Keys;
Console.WriteLine();
Console.WriteLine("打印键如下");
foreach (string s in keyColl)
{
Console.WriteLine("Key = {0}", s);
}
Console.WriteLine("\n删除key为(\"doc\")");
openWith.Remove("doc");
if (!openWith.ContainsKey("doc"))
{
Console.WriteLine("key为 \"doc\" 不存在.");
}
}
}
}
以上的输出结果为:
Immutable Collection
不可变对象集合,其中包括以下的具体类型
- ImmutableArray
- ImmutableStack
- ImmutableQueue
- ImmutableList
- ImmutableHashSet
- ImmutableSortedSet
- ImmutableDictionary<K, V>
- ImmutableSortedDictionary<K, V>
- 对不可靠的客户代码库来说,它使用安全,可以在未受信任的类库中安全的使用这些对象
- 线程安全的:immutable对象在多线程下安全,没有竞态条件
- 不需要支持可变性, 可以尽量节省空间和时间的开销. 所有的不可变集合实现都比可变集合更加有效的利用内存 (analysis)
- 可以被使用为一个常量,并且期望在未来也是保持不变的
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace SetTest
{
public class ImmutableListTest : ISet
{
public void Show()
{
ImmutableList<string> colors = ImmutableList.Create("Red", "Green", "Blue");
ImmutableList<string> colorsModified = colors.RemoveAt(1).Add("Orange");
foreach (string s in colorsModified)
{
Console.WriteLine(s);
}
var colorsModifiedBuilder = colors.ToBuilder();
colorsModifiedBuilder.Add("Black");
ImmutableList<string> colorsModified2 = colorsModifiedBuilder.ToImmutable();
Console.WriteLine();
foreach (string s in colorsModified2)
{
Console.WriteLine(s);
}
}
}
}
以上的输出结果为:
ReadOnlyCollection
只读集合
- ReadOnlyCollection只是对原对象的包装。修改原对象还是会影响当前对象。所以不是线程安全的
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace SetTest
{
public class ReadOnlyCollectionTest : ISet
{
public void Show()
{
List<string> dinosaurs = new List<string>();
dinosaurs.Add("Tyrannosaurus");
dinosaurs.Add("Amargasaurus");
dinosaurs.Add("Deinonychus");
dinosaurs.Add("Compsognathus");
ReadOnlyCollection<string> readOnlyDinosaurs =
new ReadOnlyCollection<string>(dinosaurs);
Console.WriteLine("只读集合输出结果如下:");
foreach (string dinosaur in readOnlyDinosaurs)
{
Console.WriteLine(dinosaur);
}
Console.WriteLine("\n数量: {0}", readOnlyDinosaurs.Count);
Console.WriteLine("\n是否包含(\"Deinonychus\"): {0}",
readOnlyDinosaurs.Contains("Deinonychus"));
Console.WriteLine("\nreadOnlyDinosaurs[3]: {0}",
readOnlyDinosaurs[3]);
Console.WriteLine("\n(\"Compsognathus\")位置: {0}",
readOnlyDinosaurs.IndexOf("Compsognathus"));
Console.WriteLine("\n插入到原集合中:");
Console.WriteLine("Insert(2, \"Oviraptor\")");
dinosaurs.Insert(2, "Oviraptor");
Console.WriteLine("只读集合输出结果如下:");
foreach (string dinosaur in readOnlyDinosaurs)
{
Console.WriteLine(dinosaur);
}
string[] dinoArray = new string[readOnlyDinosaurs.Count + 2];
readOnlyDinosaurs.CopyTo(dinoArray, 1);
Console.WriteLine("\n复制的只读集合包含{0}个:",
dinoArray.Length);
foreach (string dinosaur in dinoArray)
{
Console.WriteLine("\"{0}\"", dinosaur);
}
}
}
}
以上代码的输出结果为:
Concurrent Collection
其中包括以下具体类型
BlockingCollection
ConcurrentDictionary<TKey,TValue>
ConcurrentQueue
ConcurrentStack
ConcurrentBag
IProducerConsumerCollection
- BlockingCollection
提供具有阻塞和限制功能的线程安全集合,实现IProducerConsumerCollection
BoundedCapacity:获取集合限定容量,在通过构造函数时可以指定集合的容量大小。
IsAddingCompleted:判断是否已经标记完成添加,
IsCompleted:判断是否标记完成添加并且为空。
Add:将项添加到集合中。
CompleteAdding:将集合标记为完成添加。调用该函数后IsAddingCompleted为true,如果集合中包含的项数为0,那么IsCompleted也为true。
Take:从集合中移除一项。
TryAdd:尝试将项加入集合
TryTake:尝试从集合中移除项。
- ConcurrentBag
提供可供多个线程同时安全访问的无序包。对应非泛型列表List
包(Bag)和数据上的集(Set)的区别是包可包含重复元素,而集中不能包含重复元素。
Count:获取无序包中的元素数量
IsEmpty:判断无序包是否为空。
TryPeek:从无序包中获取一个元素,但不进行移除。
TryTask:从无序包获取一个元素并移除。
- ConcurrentDictionary<TKey,TValue>
提供可供多线程同时访问的键值对的线程安全集合,对应Dictionary<TKey, TValue>。
IsEmpty:判断字典是否为空。
AddOrUpdate:将键值对添加到字典中,如果Key值已经存在,则更新Value值。
Clear:将所有键值对从字典中移除。
GetOrAdd:如果键不存在,则将键值对添加到字典中并返回Value值,如果键已经存在,则返回现有值,不会更新现有值。
TryUpdate:尝试更新键对应的Value值。
- ConcurrentQueue
队列,提供线程安全的先进先出(FIFO)集合,对应Queue
Enqueue:将对象添加到队列末尾处。
TryDequeue:尝试移除并返回位于队列开头处的对象。
TryPeek:尝试返回队列开头处的对象但不将其移除。
- ConcurrentStack
栈,提供线程安全的后进先出(LIFO)集合,对应Stack
Push:将对象插入栈顶部。
PushRange:将多个对象插入栈的顶部。
TryPeek:尝试返回栈顶部的对象但不将其移除。
TryPop:尝试弹出并返回栈顶部的对象。
TryPopRange:尝试弹出并返回栈顶部的多个对象。