在本章中,我们将介绍 C# 集合。 .NET 框架为数据存储和检索提供了专门的类。 在前面的章节中,我们描述了数组。 集合是对数组的增强。
C# 中有三种不同的集合类型:
- 标准
- 通用的
- 同时
标准集合可在System.Collections
下找到。 它们不将元素存储为特定类型的对象,而是存储为Object
类型的对象。 标准集合包括ArrayList
,Hashtable
,Queue
和Stack
。
The generic collections are found under System.Collections.Generic
. Generic collections are more flexible and are the preferred way to work with data. Generics enhance code reuse, type safety, and performance. The generic collections include Dictionary<T, T>
, List<T>
, Queue<T>
, SortedList<T>
, and Stack<T>
.
并发集合包括BlockingCollection<T>
,ConcurrentDictionary<T, T>
,ConcurrentQueue<T>
和ConcurrentStack<T>
。
通用编程是一种计算机编程样式,其中,算法根据待指定的后来的类型编写,然后在需要作为参数提供的特定类型时实例化。 这种方法由 Ada 于 1983 年率先提出,它允许编写仅在使用时所使用的类型集不同的常见功能或类型,从而减少了重复。 (维基百科)
C# ArrayList
ArrayList
是标准System.Collections
命名空间的集合。 它是一个动态数组。 它提供对元素的随机访问。 添加数据后,ArrayList
会自动扩展。 与数组不同,ArrayList
可以保存多种数据类型的数据。 ArrayList
中的元素通过整数索引访问。 索引从零开始。 ArrayList
末尾的元素索引以及插入和删除操作需要花费固定的时间。 在动态数组的中间插入或删除元素的成本更高。 这需要线性时间。
Program.cs
using System;
using System.Collections;
namespace ArrayListEx
{
class Empty { }
class Program
{
static void Main(string[] args)
{
var data = new ArrayList();
data.Add("Visual Basic");
data.Add(344);
data.Add(55);
data.Add(new Empty());
data.Remove(55);
foreach (object el in data)
{
Console.WriteLine(el);
}
}
}
}
在上面的示例中,我们创建了一个ArrayList
集合。 我们添加了一些元素。 它们具有各种数据类型,字符串,整数和类对象。
using System.Collections;
为了使用ArrayList
集合,我们需要使用System.Collections
命名空间。
var data = new ArrayList();
创建一个ArrayList
集合。
data.Add("Visual Basic");
data.Add(344);
data.Add(55);
data.Add(new Empty());
data.Remove(55);
我们使用Add()
方法向数组添加四个元素。
data.Remove(55);
我们使用Remove()
方法删除一个元素。
foreach(object el in data)
{
Console.WriteLine(el);
}
我们遍历数组并将其元素打印到控制台。
$ dotnet run
Visual Basic
344
ArrayListEx.Empty
这是示例的输出。
C# List
List
是可以通过索引访问的对象的强类型列表。 可以在System.Collections.Generic
命名空间下找到。
Program.cs
using System;
using System.Collections.Generic;
namespace ListEx
{
class Program
{
static void Main(string[] args)
{
var langs = new List<string>();
langs.Add("Java");
langs.Add("C#");
langs.Add("C");
langs.Add("C++");
langs.Add("Ruby");
langs.Add("Javascript");
Console.WriteLine(langs.Contains("C#"));
Console.WriteLine(langs[1]);
Console.WriteLine(langs[2]);
langs.Remove("C#");
langs.Remove("C");
Console.WriteLine(langs.Contains("C#"));
langs.Insert(4, "Haskell");
langs.Sort();
foreach (string lang in langs)
{
Console.WriteLine(lang);
}
}
}
}
在前面的示例中,我们使用List
集合。
using System.Collections.Generic;
List
集合位于System.Collections.Generic
命名空间中。
var langs = new List<string>();
将创建一个通用动态数组。 我们指定将使用在< >字符内指定类型的字符串。
langs.Add("Java");
langs.Add("C#");
langs.Add("C");
...
我们使用Add()
方法将元素添加到列表中。
Console.WriteLine(langs.Contains("C#"));
我们使用Contains()
方法检查列表是否包含特定的字符串。
Console.WriteLine(langs[1]);
Console.WriteLine(langs[2]);
我们使用索引符号访问 List 的第二个和第三个元素。
langs.Remove("C#");
langs.Remove("C");
我们从列表中删除两个字符串。
langs.Insert(4, "Haskell");
我们在特定位置插入一个字符串。
langs.Sort();
我们使用Sort()
方法对元素进行排序。
$ dotnet run
True
C#
C
False
C++
Haskell
Java
Javascript
Ruby
这是示例的结果。
C# 集合初始值设定项
集合初始化器允许在对象创建期间为{}
括号指定集合的元素。
Program.cs
using System;
using System.Collections.Generic;
using System.Linq;
namespace CollectionInitializer
{
class Program
{
static void Main(string[] args)
{
var vals = new List<int>() { 1, 2, 3, 4, 5, 6, 7 };
int sum = vals.Sum();
Console.WriteLine(sum);
}
}
}
该示例创建一个列表并打印其总和。 列表的元素在集合初始化程序中指定。
$ dotnet run
28
这是输出。
C# SortedList
SortedList<T, T>
表示已排序的键/值对的集合。
Program.cs
using System;
using System.Collections.Generic;
namespace SortedListEx
{
class Program
{
static void Main(string[] args)
{
var sorted = new SortedList<string, int>();
sorted.Add("coins", 3);
sorted.Add("books", 41);
sorted.Add("spoons", 5);
if (sorted.ContainsKey("books"))
{
Console.WriteLine("There are books in the list");
}
foreach (var pair in sorted)
{
Console.WriteLine(pair);
}
}
}
}
该示例使用排序列表来组织项目。
var sorted = new SortedList<string, int>();
排序的列表具有字符串键和整数值。
if (sorted.ContainsKey("books"))
{
Console.WriteLine("There are books in the list");
}
使用ContainsKey()
,我们检查集合中是否有书籍。
foreach (var pair in sorted)
{
Console.WriteLine(pair);
}
使用 foreach 循环,我们遍历集合并打印其对。
$ dotnet run
There are books in the list
[books, 41]
[coins, 3]
[spoons, 5]
C# LinkedList
LinkedList
是 C# 中的通用双链表。 LinkedList 仅允许顺序访问。 LinkedList
允许进行固定时间的插入或移除,但只能顺序访问元素。 由于链接列表需要额外的存储空间以供参考,因此对于诸如字符之类的小型数据项列表来说,它们是不切实际的。 与动态数组不同,可以将任意数量的项目添加到链接列表(当然受内存限制)而无需重新分配,这是一项昂贵的操作。
Program.cs
using System;
using System.Collections.Generic;
namespace LinkedListEx
{
class Program
{
static void Main(string[] args)
{
var nums = new LinkedList<int>();
nums.AddLast(23);
nums.AddLast(34);
nums.AddLast(33);
nums.AddLast(11);
nums.AddLast(6);
nums.AddFirst(9);
nums.AddFirst(7);
LinkedListNode<int> node = nums.Find(6);
nums.AddBefore(node, 5);
foreach (int num in nums)
{
Console.WriteLine(num);
}
}
}
}
这是一个LinkedList
示例,其中包含一些方法。
var nums = new LinkedList<int>();
这是一个整数LinkedList
。
nums.AddLast(23);
...
nums.AddFirst(7);
我们使用AddLast()
和AddFirst()
方法填充链表。
LinkedListNode<int> node = nums.Find(6);
nums.AddBefore(node, 5);
LinkedList
由节点组成。 我们找到一个特定的节点,并在其之前添加一个元素。
foreach(int num in nums)
{
Console.WriteLine(num);
}
我们正在将所有元素打印到控制台。
$ dotnet run
7
9
23
34
33
11
5
6
C# dictionary
dictionary
,也称为关联数组,是唯一键和值的集合,其中每个键与一个值相关联。 检索和添加值非常快。 字典占用更多内存,因为每个值都有一个键。
Program.cs
using System;
using System.Collections.Generic;
namespace DictionaryEx
{
class Program
{
static void Main(string[] args)
{
var domains = new Dictionary<string, string>();
domains.Add("de", "Germany");
domains.Add("sk", "Slovakia");
domains.Add("us", "United States");
domains.Add("ru", "Russia");
domains.Add("hu", "Hungary");
domains.Add("pl", "Poland");
Console.WriteLine(domains["sk"]);
Console.WriteLine(domains["de"]);
Console.WriteLine("Dictionary has {0} items",
domains.Count);
Console.WriteLine("Keys of the dictionary:");
var keys = new List<string>(domains.Keys);
foreach (string key in keys)
{
Console.WriteLine("{0}", key);
}
Console.WriteLine("Values of the dictionary:");
var vals = new List<string>(domains.Values);
foreach (string val in vals)
{
Console.WriteLine("{0}", val);
}
Console.WriteLine("Keys and values of the dictionary:");
foreach (KeyValuePair<string, string> kvp in domains)
{
Console.WriteLine("Key = {0}, Value = {1}",
kvp.Key, kvp.Value);
}
}
}
}
我们有一本字典,用于将域名映射到其国家名称。
var domains = new Dictionary<string, string>();
我们创建一个包含字符串键和值的字典。
domains.Add("de", "Germany");
domains.Add("sk", "Slovakia");
domains.Add("us", "United States");
...
我们将一些数据添加到字典中。 第一个字符串是键。 第二是价值。
Console.WriteLine(domains["sk"]);
Console.WriteLine(domains["de"]);
在这里,我们通过它们的键检索两个值。
Console.WriteLine("Dictionary has {0} items",
domains.Count);
我们通过参考Count
属性来打印项目数。
var keys = new List<string>(domains.Keys);
foreach(string key in keys)
{
Console.WriteLine("{0}", key);
}
这些行从字典中检索所有键。
var vals = new List<string>(domains.Values);
foreach(string val in vals)
{
Console.WriteLine("{0}", val);
}
这些行从字典中检索所有值。
foreach(KeyValuePair<string, string> kvp in domains)
{
Console.WriteLine("Key = {0}, Value = {1}",
kvp.Key, kvp.Value);
}
最后,我们同时打印字典的键和值。
$ dotnet run
Slovakia
Germany
Dictionary has 6 items
Keys of the dictionary:
de
sk
us
ru
hu
pl
Values of the dictionary:
Germany
Slovakia
United States
Russia
Hungary
Poland
Keys and values of the dictionary:
Key = de, Value = Germany
Key = sk, Value = Slovakia
Key = us, Value = United States
Key = ru, Value = Russia
Key = hu, Value = Hungary
Key = pl, Value = Poland
这是示例的输出。
C# queue
queue
是先进先出(FIFO)数据结构。 添加到队列中的第一个元素将是第一个要删除的元素。 队列可以用于处理消息出现时的消息,也可以用于消息到达时为客户服务的消息。 首先服务的是第一个客户。
Program.cs
using System;
using System.Collections.Generic;
namespace QueueEx
{
class Program
{
static void Main(string[] args)
{
var msgs = new Queue<string>();
msgs.Enqueue("Message 1");
msgs.Enqueue("Message 2");
msgs.Enqueue("Message 3");
msgs.Enqueue("Message 4");
msgs.Enqueue("Message 5");
Console.WriteLine(msgs.Dequeue());
Console.WriteLine(msgs.Peek());
Console.WriteLine(msgs.Peek());
Console.WriteLine();
foreach (string msg in msgs)
{
Console.WriteLine(msg);
}
}
}
}
在我们的示例中,我们有一个包含消息的队列。
var msgs = new Queue<string>();
创建字符串队列。
msgs.Enqueue("Message 1");
msgs.Enqueue("Message 2");
...
Enqueue()
将消息添加到队列末尾。
Console.WriteLine(msgs.Dequeue());
Dequeue()
方法删除并返回队列开头的项目。
Console.WriteLine(msgs.Peek());
Peek()
方法从队列中返回下一项,但不会将其从集合中删除。
$ dotnet run
Message 1
Message 2
Message 2
Message 2
Message 3
Message 4
Message 5
Dequeue()
方法从集合中删除“消息 1”。 Peek()
方法没有。 “消息 2”保留在集合中。
C# Stack
堆栈是后进先出(LIFO)数据结构。 添加到队列中的最后一个元素将是第一个要删除的元素。 C 语言使用堆栈将本地数据存储在函数中。 实现计算器时,还将使用该堆栈。
Program.cs
using System;
namespace StackEx
{
class Program
{
static void Main(string[] args)
{
var myStack = new Stack<int>();
myStack.Push(1);
myStack.Push(4);
myStack.Push(3);
myStack.Push(6);
myStack.Push(4);
Console.WriteLine(myStack.Pop());
Console.WriteLine(myStack.Peek());
Console.WriteLine(myStack.Peek());
Console.WriteLine();
foreach (int item in myStack)
{
Console.WriteLine(item);
}
}
}
}
上面有一个简单的堆栈示例。
var myStack = new Stack<int>();
创建一个Stack
数据结构。
myStack.Push(1);
myStack.Push(4);
...
Push()
方法将一个项目添加到堆栈的顶部。
Console.WriteLine(stc.Pop());
Pop()
方法从堆栈顶部删除并返回该项目。
Console.WriteLine(myStack.Peek());
Peek()
方法从堆栈顶部返回该项目。 它不会删除它。
$ dotnet run
4
6
6
6
3
4
1
这是程序的输出。