集合与泛型
泛型概念与动机
泛型允许我们编写类型参数化的代码,实现类型安全与代码复用。在没有泛型之前,ArrayList存储object类型,值类型存入时会装箱(boxing),取出时需拆箱(unboxing),既损失性能又缺乏编译期类型检查。
示例
CSHARP
using System;
using System.Collections;
ArrayList list = new ArrayList();
list.Add(42);
list.Add("hello");
int value = (int)list[0];
foreach (object item in list)
{
Console.WriteLine(item);
}
TEXT
42
hello
泛型版本则在编译期确定元素类型,无需装箱拆箱,错误在编译时即可发现:
CSHARP
using System;
using System.Collections.Generic;
List<int> numbers = new List<int>();
numbers.Add(42);
foreach (int n in numbers)
{
Console.WriteLine(n);
}
TEXT
42
List<T> 动态列表
List<T>是最常用的泛型集合,支持动态扩容的有序列表,提供丰富的增删查改方法。
示例
CSHARP
using System;
using System.Collections.Generic;
List<string> fruits = new List<string> { "apple", "banana", "cherry" };
fruits.Add("date");
fruits.AddRange(new string[] { "elderberry", "fig" });
fruits.Insert(1, "blueberry");
bool hasApple = fruits.Contains("apple");
int idx = fruits.IndexOf("cherry");
string found = fruits.Find(f => f.StartsWith("b"));
List<string> allB = fruits.FindAll(f => f.StartsWith("b"));
fruits.Sort();
fruits.ForEach(f => Console.WriteLine(f));
Console.WriteLine("---");
Console.WriteLine($"Count: {fruits.Count}");
Console.WriteLine($"Has apple: {hasApple}");
Console.WriteLine($"Cherry index: {idx}");
Console.WriteLine($"First b-word: {found}");
Console.WriteLine($"All b-words: {string.Join(", ", allB)}");
TEXT
apple
blueberry
banana
cherry
date
elderberry
fig
---
Count: 7
Has apple: True
Cherry index: 3
First b-word: blueberry
All b-words: blueberry, banana
删除元素
CSHARP
using System;
using System.Collections.Generic;
List<int> nums = new List<int> { 10, 20, 30, 40, 50 };
nums.Remove(30);
nums.RemoveAt(0);
Console.WriteLine(string.Join(", ", nums));
Console.WriteLine($"Count: {nums.Count}");
TEXT
20, 40, 50
Count: 3
Dictionary<TKey, TValue> 字典
Dictionary<TKey, TValue>存储键值对,通过键快速查找值,查找时间接近O(1)。
示例
CSHARP
using System;
using System.Collections.Generic;
Dictionary<string, int> scores = new Dictionary<string, int>
{
{ "Alice", 90 },
{ "Bob", 85 }
};
scores.Add("Charlie", 78);
scores["Bob"] = 92;
bool exists = scores.ContainsKey("Alice");
if (scores.TryGetValue("Dave", out int daveScore))
{
Console.WriteLine($"Dave: {daveScore}");
}
else
{
Console.WriteLine("Dave not found");
}
scores.Remove("Charlie");
Console.WriteLine($"Count: {scores.Count}");
Console.WriteLine("---Keys---");
foreach (string key in scores.Keys)
{
Console.WriteLine(key);
}
Console.WriteLine("---Values---");
foreach (int val in scores.Values)
{
Console.WriteLine(val);
}
Console.WriteLine("---Pairs---");
foreach (KeyValuePair<string, int> kv in scores)
{
Console.WriteLine($"{kv.Key}: {kv.Value}");
}
TEXT
Dave not found
Count: 2
---Keys---
Alice
Bob
---Values---
90
92
---Pairs---
Alice: 90
Bob: 92
泛型方法与泛型类
泛型不限于集合,方法和类也可以使用类型参数,使算法适用于多种类型。
示例
CSHARP
using System;
T Max<T>(T a, T b) where T : IComparable<T>
{
return a.CompareTo(b) >= 0 ? a : b;
}
Console.WriteLine(Max(3, 7));
Console.WriteLine(Max("apple", "banana"));
TEXT
7
banana
泛型类示例:
CSHARP
using System;
class Box<T>
{
private T _value;
public Box(T value)
{
_value = value;
}
public T Value => _value;
public override string ToString() => $"Box<{typeof(T).Name}>: {_value}";
}
Box<int> intBox = new Box<int>(42);
Box<string> strBox = new Box<string>("hello");
Console.WriteLine(intBox);
Console.WriteLine(strBox);
TEXT
Box<Int32>: 42
Box<String>: hello
泛型约束
通过where关键字对类型参数施加约束,限定可使用的类型范围。
示例
CSHARP
using System;
class Repository<T> where T : class, new()
{
public T Create()
{
T instance = new T();
Console.WriteLine($"Created: {typeof(T).Name}");
return instance;
}
}
class Player
{
public string Name { get; set; } = "Unknown";
}
Repository<Player> repo = new Repository<Player>();
Player p = repo.Create();
Console.WriteLine($"Name: {p.Name}");
TEXT
Created: Player
Name: Unknown
常用约束一览:
| 约束 | 说明 |
|---|---|
where T : class |
T必须是引用类型 |
where T : struct |
T必须是值类型 |
where T : new() |
T必须有无参公共构造函数 |
where T : IFoo |
T必须实现IFoo接口 |
where T : Base |
T必须继承Base类 |
Queue<T> 队列
队列是先进先出(FIFO)数据结构,Enqueue入队,Dequeue出队。
示例
CSHARP
using System;
using System.Collections.Generic;
Queue<string> queue = new Queue<string>();
queue.Enqueue("first");
queue.Enqueue("second");
queue.Enqueue("third");
string front = queue.Peek();
Console.WriteLine($"Peek: {front}");
while (queue.Count > 0)
{
Console.WriteLine($"Dequeue: {queue.Dequeue()}");
}
TEXT
Peek: first
Dequeue: first
Dequeue: second
Dequeue: third
Stack<T> 栈
栈是后进先出(LIFO)数据结构,Push入栈,Pop出栈。
示例
CSHARP
using System;
using System.Collections.Generic;
Stack<int> stack = new Stack<int>();
stack.Push(10);
stack.Push(20);
stack.Push(30);
int top = stack.Peek();
Console.WriteLine($"Peek: {top}");
while (stack.Count > 0)
{
Console.WriteLine($"Pop: {stack.Pop()}");
}
TEXT
Peek: 30
Pop: 30
Pop: 20
Pop: 10
集合初始化器
集合初始化器允许在创建集合时直接填充元素,语法简洁直观。
示例
CSHARP
using System;
using System.Collections.Generic;
List<int> nums = new List<int> { 1, 2, 3, 4, 5 };
Dictionary<string, double> prices = new Dictionary<string, double>
{
{ "coffee", 3.5 },
{ "tea", 2.8 },
{ "juice", 4.0 }
};
Console.WriteLine(string.Join(", ", nums));
foreach (var kv in prices)
{
Console.WriteLine($"{kv.Key}: ${kv.Value}");
}
TEXT
1, 2, 3, 4, 5
coffee: $3.5
tea: $2.8
juice: $4
IEnumerable 与迭代器初识
IEnumerable<T>是所有可遍历集合的基础接口,foreach循环正是依赖它工作。使用yield return可以轻松编写自定义迭代器(详细内容见第35课)。
示例
CSHARP
using System;
using System.Collections.Generic;
IEnumerable<int> GetRange(int start, int end)
{
for (int i = start; i <= end; i++)
{
yield return i;
}
}
foreach (int n in GetRange(1, 5))
{
Console.WriteLine(n);
}
TEXT
1
2
3
4
5
💡
yield return让方法变为迭代器,每次调用返回一个值并暂停,下次迭代从暂停处继续。完整讲解见第35课。
❓ 常见问题
Q List<T>和数组有什么区别?
A List<T>可动态增删元素,数组长度固定;List内部基于数组实现,扩容时自动创建更大数组并复制元素。
Q Dictionary添加重复键会怎样?
A 使用Add方法添加重复键会抛出ArgumentException;使用索引器赋值
dict[key] = value则会覆盖已有值。Q where T : class和where T : struct能同时使用吗?
A 不能,class约束引用类型,struct约束值类型,二者互斥。
Q Queue和Stack能用foreach遍历吗?
A 可以,foreach遍历不会移除元素,仅读取。Dequeue和Pop才会移除。
Q 泛型约束where T : new()为什么需要?
A 保证可以在方法内使用
new T()创建实例,没有此约束编译器不允许对T使用new操作符。📖 小节
- 泛型提供类型安全与代码复用,避免装箱拆箱开销
List<T>是动态数组,支持增删查改、排序、查找等丰富操作Dictionary<TKey, TValue>以键值对存储,提供O(1)查找性能- 泛型方法与类让算法和数据结构适用于多种类型
where约束限定类型参数的范围:class、struct、new()、接口、基类Queue<T>先进先出,Stack<T>后进先出- 集合初始化器简化集合创建与填充
IEnumerable<T>是遍历基础,yield return可创建迭代器
📝 作业
- 创建一个
List<double>,添加5个成绩,使用FindAll筛选出80分以上的成绩并输出 - 创建一个
Dictionary<string, string>存储3个省会城市与省份的对应关系,实现根据城市名查找省份(使用TryGetValue) - 编写泛型方法
T Clamp<T>(T value, T min, T max) where T : IComparable<T>,将值限制在[min, max]范围内 - 使用
Queue<string>模拟打印队列:入队3个文档名,依次出队并打印 - 编写一个
IEnumerable<int>迭代器方法,使用yield return生成斐波那契数列的前10项



