147.md 14.9 KB
Newer Older
W
wizardforcel 已提交
1 2 3 4 5 6 7 8 9
# C# 集合

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

在本章中,我们将介绍 C# 集合。 .NET 框架为数据存储和检索提供了专门的类。 在前面的章节中,我们描述了数组。 集合是对数组的增强。

C# 中有三种不同的集合类型:

*   标准
W
wizardforcel 已提交
10 11
*   泛型
*   并发
W
wizardforcel 已提交
12 13 14 15 16

标准集合可在`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>`.

W
wizardforcel 已提交
17
并发集合包括`BlockingCollection<T>``ConcurrentDictionary<T, T>``ConcurrentQueue<T>``ConcurrentStack<T>`
W
wizardforcel 已提交
18

W
wizardforcel 已提交
19
泛型编程是一种计算机编程样式,其中,算法根据待指定的后来的类型编写,然后在需要作为参数提供的特定类型时实例化。 这种方法由 Ada 于 1983 年率先提出,它允许编写仅在使用时所使用的类型集不同的常见功能或类型,从而减少了重复。 (维基百科)
W
wizardforcel 已提交
20

W
wizardforcel 已提交
21
## C# `ArrayList`
W
wizardforcel 已提交
22

W
wizardforcel 已提交
23
`ArrayList`是标准`System.Collections`命名空间的集合。 它是一个动态数组。 它提供对元素的随机访问。 添加数据后,`ArrayList`会自动扩展。 与数组不同,`ArrayList`可以保存多种数据类型的数据。 `ArrayList`中的元素通过整数索引访问。 索引从零开始。 `ArrayList`末尾的元素索引以及插入和删除操作需要花费固定的时间。 在动态数组的中间插入或删除元素的成本更高。 这需要线性时间。
W
wizardforcel 已提交
24 25 26

`Program.cs`

W
wizardforcel 已提交
27
```cs
W
wizardforcel 已提交
28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
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`集合。 我们添加了一些元素。 它们具有各种数据类型,字符串,整数和类对象。

W
wizardforcel 已提交
59
```cs
W
wizardforcel 已提交
60 61 62 63 64 65
using System.Collections;

```

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

W
wizardforcel 已提交
66
```cs
W
wizardforcel 已提交
67 68 69 70 71 72
var data = new ArrayList();

```

创建一个`ArrayList`集合。

W
wizardforcel 已提交
73
```cs
W
wizardforcel 已提交
74 75 76 77 78 79 80 81 82 83
data.Add("Visual Basic");
data.Add(344);
data.Add(55);
data.Add(new Empty());
data.Remove(55);

```

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

W
wizardforcel 已提交
84
```cs
W
wizardforcel 已提交
85 86 87 88 89 90
data.Remove(55);

```

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

W
wizardforcel 已提交
91
```cs
W
wizardforcel 已提交
92 93 94 95 96 97 98 99 100
foreach(object el in data)
{
    Console.WriteLine(el);
}

```

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

W
wizardforcel 已提交
101
```cs
W
wizardforcel 已提交
102 103 104 105 106 107 108 109 110
$ dotnet run
Visual Basic
344
ArrayListEx.Empty

```

这是示例的输出。

W
wizardforcel 已提交
111
## C# `List`
W
wizardforcel 已提交
112 113 114 115 116

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

`Program.cs`

W
wizardforcel 已提交
117
```cs
W
wizardforcel 已提交
118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161
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`集合。

W
wizardforcel 已提交
162
```cs
W
wizardforcel 已提交
163 164 165 166 167 168
using System.Collections.Generic;

```

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

W
wizardforcel 已提交
169
```cs
W
wizardforcel 已提交
170 171 172 173
var langs = new List<string>();

```

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

W
wizardforcel 已提交
176
```cs
W
wizardforcel 已提交
177 178 179 180 181 182 183 184 185
langs.Add("Java");
langs.Add("C#");
langs.Add("C");
...

```

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

W
wizardforcel 已提交
186
```cs
W
wizardforcel 已提交
187 188 189 190 191 192
Console.WriteLine(langs.Contains("C#"));

```

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

W
wizardforcel 已提交
193
```cs
W
wizardforcel 已提交
194 195 196 197 198
Console.WriteLine(langs[1]);
Console.WriteLine(langs[2]);

```

W
wizardforcel 已提交
199
我们使用索引符号访问`List`的第二个和第三个元素。
W
wizardforcel 已提交
200

W
wizardforcel 已提交
201
```cs
W
wizardforcel 已提交
202 203 204 205 206 207 208
langs.Remove("C#");
langs.Remove("C");

```

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

W
wizardforcel 已提交
209
```cs
W
wizardforcel 已提交
210 211 212 213 214 215
langs.Insert(4, "Haskell");

```

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

W
wizardforcel 已提交
216
```cs
W
wizardforcel 已提交
217 218 219 220 221 222
langs.Sort();

```

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

W
wizardforcel 已提交
223
```cs
W
wizardforcel 已提交
224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244
$ dotnet run
True
C#
C
False
C++
Haskell
Java
Javascript
Ruby

```

这是示例的结果。

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

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

`Program.cs`

W
wizardforcel 已提交
245
```cs
W
wizardforcel 已提交
246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266
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);
        }
    }
}

```

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

W
wizardforcel 已提交
269
```cs
W
wizardforcel 已提交
270 271 272 273 274 275 276
$ dotnet run
28

```

这是输出。

W
wizardforcel 已提交
277
## C# `SortedList`
W
wizardforcel 已提交
278

W
wizardforcel 已提交
279
`SortedList<T, T>`表示已排序的键/值对的集合。
W
wizardforcel 已提交
280 281 282

`Program.cs`

W
wizardforcel 已提交
283
```cs
W
wizardforcel 已提交
284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315
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);
            }
        }
    }
}

```

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

W
wizardforcel 已提交
316
```cs
W
wizardforcel 已提交
317 318 319 320 321 322
var sorted = new SortedList<string, int>();

```

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

W
wizardforcel 已提交
323
```cs
W
wizardforcel 已提交
324 325 326 327 328 329 330 331 332
if (sorted.ContainsKey("books"))
{
    Console.WriteLine("There are books in the list");
}

```

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

W
wizardforcel 已提交
333
```cs
W
wizardforcel 已提交
334 335 336 337 338 339 340
foreach (var pair in sorted)
{
    Console.WriteLine(pair);
}

```

W
wizardforcel 已提交
341
使用`foreach`循环,我们遍历集合并打印其对。
W
wizardforcel 已提交
342

W
wizardforcel 已提交
343
```cs
W
wizardforcel 已提交
344 345 346 347 348 349 350 351 352 353
$ dotnet run
There are books in the list
[books, 41]
[coins, 3]
[spoons, 5]

```

This is the output.

W
wizardforcel 已提交
354
## C# `LinkedList`
W
wizardforcel 已提交
355

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

`Program.cs`

W
wizardforcel 已提交
360
```cs
W
wizardforcel 已提交
361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394
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`示例,其中包含一些方法。

W
wizardforcel 已提交
395
```cs
W
wizardforcel 已提交
396 397 398 399 400 401
var nums = new LinkedList<int>();

```

这是一个整数`LinkedList`

W
wizardforcel 已提交
402
```cs
W
wizardforcel 已提交
403 404 405 406 407 408 409 410
nums.AddLast(23);
...
nums.AddFirst(7);

```

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

W
wizardforcel 已提交
411
```cs
W
wizardforcel 已提交
412 413 414 415 416 417 418
LinkedListNode<int> node = nums.Find(6);
nums.AddBefore(node, 5);

```

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

W
wizardforcel 已提交
419
```cs
W
wizardforcel 已提交
420 421 422 423 424 425 426 427 428
foreach(int num in nums)
{
    Console.WriteLine(num);
}

```

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

W
wizardforcel 已提交
429
```cs
W
wizardforcel 已提交
430 431 432 433 434 435 436 437 438 439 440 441 442 443
$ dotnet run
7
9
23
34
33
11
5
6

```

This is the output.

W
wizardforcel 已提交
444
## C# `dictionary`
W
wizardforcel 已提交
445 446 447 448 449

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

`Program.cs`

W
wizardforcel 已提交
450
```cs
W
wizardforcel 已提交
451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507
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);
            }
        }
    }
}

```

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

W
wizardforcel 已提交
508
```cs
W
wizardforcel 已提交
509 510 511 512 513 514
var domains = new Dictionary<string, string>();

```

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

W
wizardforcel 已提交
515
```cs
W
wizardforcel 已提交
516 517 518 519 520 521 522
domains.Add("de", "Germany");
domains.Add("sk", "Slovakia");
domains.Add("us", "United States");
...

```

W
wizardforcel 已提交
523
我们将一些数据添加到字典中。 第一个字符串是键。 第二是值。
W
wizardforcel 已提交
524

W
wizardforcel 已提交
525
```cs
W
wizardforcel 已提交
526 527 528 529 530 531 532
Console.WriteLine(domains["sk"]);
Console.WriteLine(domains["de"]);

```

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

W
wizardforcel 已提交
533
```cs
W
wizardforcel 已提交
534 535 536 537 538
Console.WriteLine("Dictionary has {0} items",
    domains.Count);

```

W
wizardforcel 已提交
539
我们通过引用`Count`属性来打印项目数。
W
wizardforcel 已提交
540

W
wizardforcel 已提交
541
```cs
W
wizardforcel 已提交
542 543 544 545 546 547 548 549 550 551 552
var keys = new List<string>(domains.Keys);

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

```

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

W
wizardforcel 已提交
553
```cs
W
wizardforcel 已提交
554 555 556 557 558 559 560 561 562 563 564
var vals = new List<string>(domains.Values);

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

```

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

W
wizardforcel 已提交
565
```cs
W
wizardforcel 已提交
566 567 568 569 570 571 572 573 574 575
foreach(KeyValuePair<string, string> kvp in domains)
{
    Console.WriteLine("Key = {0}, Value = {1}", 
        kvp.Key, kvp.Value);
}

```

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

W
wizardforcel 已提交
576
```cs
W
wizardforcel 已提交
577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606
$ 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

```

这是示例的输出。

W
wizardforcel 已提交
607
## C# `queue`
W
wizardforcel 已提交
608 609 610 611 612

`queue`是先进先出(FIFO)数据结构。 添加到队列中的第一个元素将是第一个要删除的元素。 队列可以用于处理消息出现时的消息,也可以用于消息到达时为客户服务的消息。 首先服务的是第一个客户。

`Program.cs`

W
wizardforcel 已提交
613
```cs
W
wizardforcel 已提交
614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648
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);
            }
        }
    }
}

```

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

W
wizardforcel 已提交
649
```cs
W
wizardforcel 已提交
650 651 652 653 654 655
var msgs = new Queue<string>();

```

创建字符串队列。

W
wizardforcel 已提交
656
```cs
W
wizardforcel 已提交
657 658 659 660 661 662 663 664
msgs.Enqueue("Message 1");
msgs.Enqueue("Message 2");
...

```

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

W
wizardforcel 已提交
665
```cs
W
wizardforcel 已提交
666 667 668 669 670 671
Console.WriteLine(msgs.Dequeue());

```

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

W
wizardforcel 已提交
672
```cs
W
wizardforcel 已提交
673 674 675 676 677 678
Console.WriteLine(msgs.Peek());

```

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

W
wizardforcel 已提交
679
```cs
W
wizardforcel 已提交
680 681 682 683 684 685 686 687 688 689 690 691 692 693
$ dotnet run
Message 1
Message 2
Message 2

Message 2
Message 3
Message 4
Message 5

```

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

W
wizardforcel 已提交
694
## C# `Stack`
W
wizardforcel 已提交
695

W
wizardforcel 已提交
696
栈是后进先出(LIFO)数据结构。 添加到队列中的最后一个元素将是第一个要删除的元素。 C 语言使用栈将本地数据存储在函数中。 实现计算器时,还将使用该栈。
W
wizardforcel 已提交
697 698 699

`Program.cs`

W
wizardforcel 已提交
700
```cs
W
wizardforcel 已提交
701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732
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);
            }
        }
    }
}

```

W
wizardforcel 已提交
733
上面有一个简单的栈示例。
W
wizardforcel 已提交
734

W
wizardforcel 已提交
735
```cs
W
wizardforcel 已提交
736 737 738 739 740 741
var myStack = new Stack<int>();

```

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

W
wizardforcel 已提交
742
```cs
W
wizardforcel 已提交
743 744 745 746 747 748
myStack.Push(1);
myStack.Push(4);
...

```

W
wizardforcel 已提交
749
`Push()`方法将一个项目添加到栈的顶部。
W
wizardforcel 已提交
750

W
wizardforcel 已提交
751
```cs
W
wizardforcel 已提交
752 753 754 755
Console.WriteLine(stc.Pop());

```

W
wizardforcel 已提交
756
`Pop()`方法从栈顶部删除并返回该项目。
W
wizardforcel 已提交
757

W
wizardforcel 已提交
758
```cs
W
wizardforcel 已提交
759 760 761 762
Console.WriteLine(myStack.Peek());

```

W
wizardforcel 已提交
763
`Peek()`方法从栈顶部返回该项目。 它不会删除它。
W
wizardforcel 已提交
764

W
wizardforcel 已提交
765
```cs
W
wizardforcel 已提交
766 767 768 769 770 771 772 773 774 775 776 777 778 779 780
$ dotnet run
4
6
6

6
3
4
1

```

这是程序的输出。

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