上一篇介紹了泛型(Generics)的概念,也簡單帶到了List<T>、Dictionary、Queue、Stack。
這篇將針對 System.Collections.Generic 中最常用的集合,做更深入的說明與實作範例。
System.Collections.Generic 是 .NET 標準函式庫的一部分。
無論是Unity、ASP.NET、Console App,只要是C#專案都可以使用。
using System.Collections.Generic;
Dictionary<TKey, TValue>是以「Key → Value」配對的集合,適合需要快速查找的情境。
可以把它想成一本字典:透過單字(Key)查到解釋(Value)。
如果用List儲存玩家資料,要找到特定玩家通常需要遍歷整個List:
List<string> players = new List<string> { "Alice", "Bob", "Charlie" };
// 要找 Bob,必須一個一個比對
改用Dictionary後,可以直接透過Key取得:
Dictionary<int, string> players = new Dictionary<int, string>();
players.Add(1001, "Alice");
players.Add(1002, "Bob");
Console.WriteLine(players[1002]); // 直接取得 Bob
var inventory = new Dictionary<string, int>
{
{ "Potion", 5 },
{ "Arrow", 100 },
{ "Shield", 1 }
};
方式Key不存在時dict[key]拋出KeyNotFoundException,TryGetValue回傳false,不拋例外。
建議優先使用TryGetValue,避免程式因找不到Key而崩潰。
HashSet<T>是不允許重複元素的集合,適合需要快速判斷「是否存在」的情境。
var set = new HashSet<int>();
set.Add(1);
set.Add(2);
set.Add(2); // 無效,已存在,不會拋例外
set.Add(3);
Console.WriteLine(set.Count); // 3
Console.WriteLine(set.Contains(2)); // true
set.Remove(1);
var setA = new HashSet<int> { 1, 2, 3, 4 };
var setB = new HashSet<int> { 3, 4, 5, 6 };
setA.UnionWith(setB); // 聯集:{ 1, 2, 3, 4, 5, 6 }
setA.IntersectWith(setB); // 交集:{ 3, 4 }
setA.ExceptWith(setB); // 差集(移除與 B 相同的):{ 1, 2 }
setA.IsSubsetOf(setB); // 是否為子集
setA.Overlaps(setB); // 是否有交集
UnionWith、IntersectWith、ExceptWith都會修改自身,不會回傳新集合。
// 已解鎖的成就
HashSet<string> unlockedAchievements = new HashSet<string>();
unlockedAchievements.Add("FirstKill");
if (!unlockedAchievements.Contains("FirstKill"))
{
// 解鎖成就
}
// A* 路徑演算法中已訪問的節點
HashSet<Vector2Int> visited = new HashSet<Vector2Int>();
// 已訪問的地圖格
HashSet<int> exploredTiles = new HashSet<int>();
Queue<T>遵循 先進先出(FIFO) 原則,第一個加入的元素,第一個被取出
var queue = new Queue<string>();
queue.Enqueue("Slime"); // 加入末尾
queue.Enqueue("Goblin");
queue.Enqueue("Dragon");
Console.WriteLine(queue.Peek()); // 查看最前面,不取出:Slime
Console.WriteLine(queue.Dequeue()); // 取出最前面並移除:Slime
Console.WriteLine(queue.Count); // 2
queue.Contains("Goblin"); // true
queue.Clear();
// 怪物生成排程
Queue<string> spawnQueue = new Queue<string>();
spawnQueue.Enqueue("Slime");
spawnQueue.Enqueue("Goblin");
void SpawnNext()
{
if (spawnQueue.Count > 0)
{
string enemy = spawnQueue.Dequeue();
// 生成怪物...
}
}
// 技能施放隊列
Queue<string> skillQueue = new Queue<string>();
// NPC 對話佇列
Queue<string> dialogQueue = new Queue<string>();
Stack<T>遵循 後進先出(LIFO) 原則,最後加入的元素,第一個被取出。
var stack = new Stack<string>();
stack.Push("Main Menu"); // 加入頂部
stack.Push("Settings");
stack.Push("Audio");
Console.WriteLine(stack.Peek()); // 查看頂部,不取出:Audio
Console.WriteLine(stack.Pop()); // 取出頂部並移除:Audio
Console.WriteLine(stack.Count); // 2
stack.Contains("Settings"); // true
stack.Clear();
// 場景返回歷史(類似瀏覽器上一頁)
Stack<string> sceneHistory = new Stack<string>();
sceneHistory.Push("MainMenu");
sceneHistory.Push("Lobby");
sceneHistory.Push("GameRoom");
void GoBack()
{
if (sceneHistory.Count > 0)
{
string previousScene = sceneHistory.Pop();
// 載入上一個場景...
}
}
// Undo 系統(撤銷操作)
Stack<ICommand> undoStack = new Stack<ICommand>();
void Undo()
{
if (undoStack.Count > 0)
{
undoStack.Pop().Undo();
}
}
| 需求 | 推薦集合 |
|---|---|
| 有序、可重複、常用索引存取 | List<T> |
| 快速 Key → Value 查找 | Dictionary<TKey, TValue> |
| 去重、集合運算(聯集/交集) | HashSet<T> |
| 排程、任務佇列(先進先出) | Queue<T> |
| 撤銷、回溯、場景歷史(後進先出) | Stack<T> |
System.Collections.Generic 提供了各種不同特性的泛型集合,理解它們各自的適用情境,能讓程式碼更乾淨、效能更好。
Dictionary:查找快,適合對應關係HashSet:去重快,適合判斷是否存在Queue:先進先出,適合排程與佇列Stack:後進先出,適合撤銷與歷史記錄list.Add(item) // 加入末尾
list.AddRange(collection) // 批次加入
list.Insert(index, item) // 插入指定位置
list.Remove(item) // 移除第一個符合
list.RemoveAt(index) // 移除指定索引
list.RemoveAll(x => x > 5) // 條件移除
list.Clear() // 清空
list.Contains(item) // 是否存在
list.Find(x => x > 5) // 找第一個符合條件的元素
list.FindAll(x => x > 5) // 找所有符合條件
list.IndexOf(item) // 取得索引,找不到回傳 -1
list.Exists(x => x > 5) // 是否存在符合條件的元素
list.Sort() // 預設排序
list.Sort((a, b) => a.CompareTo(b)) // 自訂排序
list.Reverse() // 反轉
list.Count // 元素數量
list.ToArray() // 轉成陣列
dict.Add(key, value) // 加入(key 重複會例外)
dict[key] = value // 加入或覆蓋
dict.Remove(key) // 移除
dict.Clear() // 清空
dict.ContainsKey(key) // 是否有此 key
dict.ContainsValue(value) // 是否有此 value
dict.TryGetValue(key, out var val) // 安全取值(推薦)
dict[key] // 取值(key 不存在會例外)
dict.Count // 數量
dict.Keys // 所有 key 集合
dict.Values // 所有 value 集合
set.Add(item) // 加入(重複無效)
set.Remove(item) // 移除
set.Clear() // 清空
set.Contains(item) // 是否存在
set.Count // 數量
set.UnionWith(other) // 聯集(修改自身)
set.IntersectWith(other) // 交集
set.ExceptWith(other) // 差集(移除與 other 相同的)
set.IsSubsetOf(other) // 是否為子集
set.Overlaps(other) // 是否有交集
queue.Enqueue(item) // 加入末尾
queue.Dequeue() // 取出最前面(移除)
queue.Peek() // 查看最前面(不移除)
queue.Contains(item) // 是否存在
queue.Clear() // 清空
queue.Count // 數量
stack.Push(item) // 加入頂部
stack.Pop() // 取出頂部(移除)
stack.Peek() // 查看頂部(不移除)
stack.Contains(item) // 是否存在
stack.Clear() // 清空
stack.Count // 數量
// foreach 遍歷(所有集合都支援)
foreach (var item in list) { }
foreach (var kvp in dict) { }
// LINQ 搭配使用(需 using System.Linq)
list.Where(x => x > 5).ToList()
list.OrderBy(x => x).ToList()
list.FirstOrDefault(x => x > 5)
list.Any(x => x > 5)
list.All(x => x > 0)
list.Sum() / list.Max() / list.Min()