集合
集合
用于存储和管理相关对象的组。一些集合类型(例如 System.Array、System.Span
集合提供灵活的方式来使用对象组。 可按以下特征对不同的集合进行分类:
- 元素访问:可以枚举每个集合以按顺序访问每个元素。 某些集合可通过索引(元素在有序集合中的位置)访问元素。 最常见的示例是 System.Collections.Generic.List
。 其他集合可按键访问元素,其中值与单个键相关联。 最常见的示例是 System.Collections.Generic.Dictionary<TKey,TValue>。 可根据应用访问元素的方式在这些集合类型之间进行选择。
- 性能配置文件:每个集合都有不同的性能配置文件,可用于添加元素、查找元素或移除元素等操作。 可以根据应用中最常用的操作选取集合类型。
- 动态增长和收缩:大多数集合支持动态添加或移除元素。 需要注意的是,Array、System.Span
和 System.Memory 不支持。
除了这些特征之外,运行时还提供专用集合,这些集合可阻止添加或移除元素,或修改集合的元素。 其他专用集合为多线程应用中的并发访问提供安全性。
常用的集合类型:
集合类型表示收集数据的不同方式,例如哈希表、队列、堆栈、包、字典和列表。
所有集合都直接或间接基于 ICollection
在基于 IList 或直接基于 ICollection 的集合中,每个元素都只包含一个值。 这些类型包括:
- Array
- ArrayList
- List
- Queue
- ConcurrentQueue
- Stack
- ConcurrentStack
- LinkedList
在基于 IDictionary 接口的集合中,每个元素都只包含一个键和一个值。 这些类型包括:
- Hashtable
- SortedList
- SortedList<TKey,TValue>
- Dictionary<TKey,TValue>
- ConcurrentDictionary<TKey,TValue>
KeyedCollection<TKey,TItem> 类是抽象类,该类唯一的,因为它是值中嵌键的值的列表。因此,它的行为类似列表和字典。
可索引集合
可索引集合 是一个可以使用其索引访问每个元素的集合。 其索引是序列中在它之前的元素数。 因此,按索引 0 引用的元素是第一个元素,索引 1 则是第二个元素,依此而行。 这些示例使用 List
以下示例会从一个泛型列表中按索引移除元素。 它使用以降序进行循环访问的 for 语句,而不是 foreach 语句。 RemoveAt 方法将导致已移除元素后的元素索引值减小。
List<int> numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
// Remove odd numbers.
for (var index = numbers.Count - 1; index >= 0; index--)
{
if (numbers[index] % 2 == 1)
{
// Remove the element by specifying
// the zero-based index in the list.
numbers.RemoveAt(index);
}
}
// Iterate through the list.
numbers.ForEach(
number => Console.Write(number + " "));
// Output: 0 2 4 6 8
键/值对集合
使用 Dictionary<TKey,TValue> 类。 这是最常见的字典集合。 使用字典集合,可通过使用每个元素的键访问集合中的元素。 每次对字典的添加都包含一个值和与其关联的键。
以下示例创建 Dictionary 集合并通过使用 foreach 语句循环访问字典。
private static void IterateThruDictionary()
{
Dictionary<string, Element> elements = BuildDictionary();
foreach (KeyValuePair<string, Element> kvp in elements)
{
Element theElement = kvp.Value;
Console.WriteLine("key: " + kvp.Key);
Console.WriteLine("values: " + theElement.Symbol + " " +
theElement.Name + " " + theElement.AtomicNumber);
}
}
public class Element
{
public required string Symbol { get; init; }
public required string Name { get; init; }
public required int AtomicNumber { get; init; }
}
private static Dictionary<string, Element> BuildDictionary() =>
new ()
{
{"K",
new (){ Symbol="K", Name="Potassium", AtomicNumber=19}},
{"Ca",
new (){ Symbol="Ca", Name="Calcium", AtomicNumber=20}},
{"Sc",
new (){ Symbol="Sc", Name="Scandium", AtomicNumber=21}},
{"Ti",
new (){ Symbol="Ti", Name="Titanium", AtomicNumber=22}}
};
迭代器
迭代器用于对集合执行自定义迭代。 迭代器可以是一种方法,或是一个 get 访问器。 迭代器使用 yield return 语句返回集合的每一个元素,每次返回一个元素。
通过使用 foreach 语句调用迭代器。 foreach 循环的每次迭代都会调用迭代器。 迭代器中到达 yield return 语句时,会返回一个表达式,并保留当前在代码中的位置。 下次调用迭代器时,将从该位置重新开始执行。
下面的示例使用迭代器方法。 迭代器方法具有位于 for 循环中的 yield return 语句。 在 ListEvenNumbers 方法中,foreach 语句体的每次迭代都会创建对迭代器方法的调用,并将继续到下一个 yield return 语句。
private static void ListEvenNumbers()
{
foreach (int number in EvenSequence(5, 18))
{
Console.Write(number.ToString() + " ");
}
Console.WriteLine();
// Output: 6 8 10 12 14 16 18
}
private static IEnumerable<int> EvenSequence(
int firstNumber, int lastNumber)
{
// Yield even numbers in the range.
for (var number = firstNumber; number <= lastNumber; number++)
{
if (number % 2 == 0)
{
yield return number;
}
}
}
LINQ 和集合
可以使用语言集成查询 (LINQ) 来访问集合。 LINQ 查询提供筛选、排序和分组功能。
以下示例运行一个对泛型 List 的 LINQ 查询。 LINQ 查询返回一个包含结果的不同集合。
private static void ShowLINQ()
{
List<Element> elements = BuildList();
// LINQ Query.
var subset = from theElement in elements
where theElement.AtomicNumber < 22
orderby theElement.Name
select theElement;
foreach (Element theElement in subset)
{
Console.WriteLine(theElement.Name + " " + theElement.AtomicNumber);
}
// Output:
// Calcium 20
// Potassium 19
// Scandium 21
}
private static List<Element> BuildList() => new()
{
{ new(){ Symbol="K", Name="Potassium", AtomicNumber=19}},
{ new(){ Symbol="Ca", Name="Calcium", AtomicNumber=20}},
{ new(){ Symbol="Sc", Name="Scandium", AtomicNumber=21}},
{ new(){ Symbol="Ti", Name="Titanium", AtomicNumber=22}}
};
集合之间的异同之处
- 集合在存储、排序和比较元素以及执行搜索的方式方面有所不同。
- 所有集合中使用的索引都从零开始,Array 除外,它允许不从零开始的数组。
- SortedList 类和 SortedList<TKey,TValue> 泛型类提供 Hashtable 类和 Dictionary<TKey,TValue> 泛型类的已排序版本。
- 可以通过键或元素的索引访问 SortedList 或 KeyedCollection<TKey,TItem> 的元素。 只能通过元素的键访问 Hashtable 或 Dictionary<TKey,TValue> 的元素。
以下是一个Array的不从0索引开始的集合
// 创建一个不从0开始的数组
Array myArray = Array.CreateInstance(typeof(int), [3], [1]);
选择合适的集合
请务必仔细选择需要的集合类。使用错误的类型可能会限制集合的使用。
注意:请避免使用 System.Collections 命名空间中的类型。 推荐使用泛型版本和并发版本的集合,因为它们的类型安全性很高,并且还经过了其他改进。
选择合适的集合可以根据以下方面考虑:
- 是否需要顺序列表(其中通常在检索元素值后就将该元素丢弃)?。
- 是否需要按索引访问每个元素?
- 是否每个元素都包含一个值、一个键和一个值的组合或一个键和多个值的组合?
- 是否需要以与输入方式不同的方式对元素进行排序?
- 是否需要快速搜索和信息检索?
- 是否需要只接受字符串的集合?