# C# 集合

> 原文： [https://zetcode.com/lang/csharp/collections/](https://zetcode.com/lang/csharp/collections/)

在本章中，我们将介绍 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`

```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`集合。 我们添加了一些元素。 它们具有各种数据类型，字符串，整数和类对象。

```cs
using System.Collections;

```

为了使用`ArrayList`集合，我们需要使用`System.Collections`命名空间。

```cs
var data = new ArrayList();

```

创建一个`ArrayList`集合。

```cs
data.Add("Visual Basic");
data.Add(344);
data.Add(55);
data.Add(new Empty());
data.Remove(55);

```

我们使用`Add()`方法向数组添加四个元素。

```cs
data.Remove(55);

```

我们使用`Remove()`方法删除一个元素。

```cs
foreach(object el in data)
{
    Console.WriteLine(el);
}

```

我们遍历数组并将其元素打印到控制台。

```cs
$ dotnet run
Visual Basic
344
ArrayListEx.Empty

```

这是示例的输出。

## C# `List`

`List`是可以通过索引访问的对象的强类型列表。 可以在`System.Collections.Generic`命名空间下找到。

`Program.cs`

```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`集合。

```cs
using System.Collections.Generic;

```

`List`集合位于`System.Collections.Generic`命名空间中。

```cs
var langs = new List<string>();

```

将创建一个通用动态数组。 我们指定将使用在`<>`字符内指定类型的字符串。

```cs
langs.Add("Java");
langs.Add("C#");
langs.Add("C");
...

```

我们使用`Add()`方法将元素添加到列表中。

```cs
Console.WriteLine(langs.Contains("C#"));

```

我们使用`Contains()`方法检查列表是否包含特定的字符串。

```cs
Console.WriteLine(langs[1]);
Console.WriteLine(langs[2]);

```

我们使用索引符号访问`List`的第二个和第三个元素。

```cs
langs.Remove("C#");
langs.Remove("C");

```

我们从列表中删除两个字符串。

```cs
langs.Insert(4, "Haskell");

```

我们在特定位置插入一个字符串。

```cs
langs.Sort();

```

我们使用`Sort()`方法对元素进行排序。

```cs
$ dotnet run
True
C#
C
False
C++
Haskell
Java
Javascript
Ruby

```

这是示例的结果。

## C# 集合初始值设定项

集合初始化器允许在对象创建期间为`{}`括号指定集合的​​元素。

`Program.cs`

```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);
        }
    }
}

```

该示例创建一个列表并打印其总和。 列表的元素在集合初始化器中指定。

```cs
$ dotnet run
28

```

这是输出。

## C# `SortedList`

`SortedList<T, T>`表示已排序的键/值对的集合。

`Program.cs`

```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);
            }
        }
    }
}

```

该示例使用排序列表来组织项目。

```cs
var sorted = new SortedList<string, int>();

```

排序的列表具有字符串键和整数值。

```cs
if (sorted.ContainsKey("books"))
{
    Console.WriteLine("There are books in the list");
}

```

使用`ContainsKey()`，我们检查集合中是否有书籍。

```cs
foreach (var pair in sorted)
{
    Console.WriteLine(pair);
}

```

使用`foreach`循环，我们遍历集合并打印其对。

```cs
$ dotnet run
There are books in the list
[books, 41]
[coins, 3]
[spoons, 5]

```

This is the output.

## C# `LinkedList`

`LinkedList`是 C# 中的通用双链表。 `LinkedList`仅允许顺序访问。 `LinkedList`允许进行固定时间的插入或移除，但只能顺序访问元素。 由于链表需要额外的存储空间以供参考，因此对于诸如字符之类的小型数据项列表来说，它们是不切实际的。 与动态数组不同，可以将任意数量的项目添加到链表（当然受内存限制）而无需重新分配，这是一项昂贵的操作。

`Program.cs`

```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`示例，其中包含一些方法。

```cs
var nums = new LinkedList<int>();

```

这是一个整数`LinkedList`。

```cs
nums.AddLast(23);
...
nums.AddFirst(7);

```

我们使用`AddLast()`和`AddFirst()`方法填充链表。

```cs
LinkedListNode<int> node = nums.Find(6);
nums.AddBefore(node, 5);

```

`LinkedList`由节点组成。 我们找到一个特定的节点，并在其之前添加一个元素。

```cs
foreach(int num in nums)
{
    Console.WriteLine(num);
}

```

我们正在将所有元素打印到控制台。

```cs
$ dotnet run
7
9
23
34
33
11
5
6

```

This is the output.

## C# `dictionary`

`dictionary`，也称为关联数组，是唯一键和值的集合，其中每个键与一个值相关联。 检索和添加值非常快。 字典占用更多内存，因为每个值都有一个键。

`Program.cs`

```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);
            }
        }
    }
}

```

我们有一本字典，用于将域名映射到其国家名称。

```cs
var domains = new Dictionary<string, string>();

```

我们创建一个包含字符串键和值的字典。

```cs
domains.Add("de", "Germany");
domains.Add("sk", "Slovakia");
domains.Add("us", "United States");
...

```

我们将一些数据添加到字典中。 第一个字符串是键。 第二是值。

```cs
Console.WriteLine(domains["sk"]);
Console.WriteLine(domains["de"]);

```

在这里，我们通过它们的键检索两个值。

```cs
Console.WriteLine("Dictionary has {0} items",
    domains.Count);

```

我们通过引用`Count`属性来打印项目数。

```cs
var keys = new List<string>(domains.Keys);

foreach(string key in keys)
{
    Console.WriteLine("{0}", key);
}  

```

这些行从字典中检索所有键。

```cs
var vals = new List<string>(domains.Values);

foreach(string val in vals)
{
    Console.WriteLine("{0}", val);
}

```

这些行从字典中检索所有值。

```cs
foreach(KeyValuePair<string, string> kvp in domains)
{
    Console.WriteLine("Key = {0}, Value = {1}", 
        kvp.Key, kvp.Value);
}

```

最后，我们同时打印字典的键和值。

```cs
$ 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`

```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);
            }
        }
    }
}

```

在我们的示例中，我们有一个包含消息的队列。

```cs
var msgs = new Queue<string>();

```

创建字符串队列。

```cs
msgs.Enqueue("Message 1");
msgs.Enqueue("Message 2");
...

```

`Enqueue()`将消息添加到队列末尾。

```cs
Console.WriteLine(msgs.Dequeue());

```

`Dequeue()`方法删除并返回队列开头的项目。

```cs
Console.WriteLine(msgs.Peek());

```

`Peek()`方法从队列中返回下一项，但不会将其从集合中删除。

```cs
$ dotnet run
Message 1
Message 2
Message 2

Message 2
Message 3
Message 4
Message 5

```

`Dequeue()`方法从集合中删除`Message 1`。 `Peek()`方法没有。 `Message 2`保留在集合中。

## C# `Stack`

栈是后进先出（LIFO）数据结构。 添加到队列中的最后一个元素将是第一个要删除的元素。 C 语言使用栈将本地数据存储在函数中。 实现计算器时，还将使用该栈。

`Program.cs`

```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);
            }
        }
    }
}

```

上面有一个简单的栈示例。

```cs
var myStack = new Stack<int>();

```

创建一个`Stack`数据结构。

```cs
myStack.Push(1);
myStack.Push(4);
...

```

`Push()`方法将一个项目添加到栈的顶部。

```cs
Console.WriteLine(stc.Pop());

```

`Pop()`方法从栈顶部删除并返回该项目。

```cs
Console.WriteLine(myStack.Peek());

```

`Peek()`方法从栈顶部返回该项目。 它不会删除它。

```cs
$ dotnet run
4
6
6

6
3
4
1

```

这是程序的输出。

C# 教程的这一部分专门介绍 C# 中的集合。