1. 集合搜尋的基本功能
很多時候 「搜尋」 跟 「過濾」 這兩個詞會被當成一樣,但寫程式的時候其實不太一樣。
- 過濾 — 就是我們想拿到集合裡所有符合某個條件的元素(比如所有大於 10 的數字)。
- 搜尋 — 通常是我們只想找一個元素:第一個符合的、某個特定值的,或只是想知道集合裡有沒有這個東西。
如果拿圖書館來比喻:過濾就像把所有「太空」主題的書都找出來,搜尋則像問:「你們有《尤利西斯》這本書嗎?」 或 「第一本藍色封面的書在哪?」。
所有 .NET 的主流集合(陣列、List、HashSet、Dictionary 等等)都支援好幾種搜尋或檢查有沒有元素的方法。
來看看最常用的幾個方法:
| 集合 | 檢查有沒有 | 找索引 | 找元素 | 用 key 找 |
|---|---|---|---|---|
|
|
|
|
- |
(陣列) |
/ |
|
- | - |
|
|
- | - | - |
|
, |
- | - | 有索引子 |
有些方法只有特定型別才有。
2. List 搜尋:List<T>
List<T> 是最萬用的集合之一,可以隨便存元素、隨便取。這裡是幾個主要的搜尋方法:
檢查有沒有這個元素:Contains
這個方法會告訴你 List 裡有沒有這個元素:
List<string> fruits = new List<string> { "蘋果", "香蕉", "奇異果" };
bool hasKiwi = fruits.Contains("奇異果"); // true
bool hasMango = fruits.Contains("芒果"); // false
Console.WriteLine(hasKiwi); // True
Contains 其實就是一個個比,用 Equals。
找索引:IndexOf
如果你想知道元素在 List 裡的第幾個(索引):
int index = fruits.IndexOf("香蕉"); // 1(從 0 開始算)
int absentIndex = fruits.IndexOf("西瓜"); // -1(找不到)
Console.WriteLine(index);
Console.WriteLine(absentIndex);
找第一個符合條件的:Find
你可以根據條件找,不一定要指定值!用 Find(或它的兄弟 FindIndex):
// 找第一個名字長度超過 4 的水果
string longFruit = fruits.Find(fruit => fruit.Length > 4); // "蘋果"
Console.WriteLine(longFruit);
注意:如果沒找到,會回傳型別的預設值(參考型別就是 null)。
找多個元素:FindAll
如果你想過濾(找出所有符合的),用 FindAll:
// 找所有名字裡有 'и' 的水果
List<string> withI = fruits.FindAll(f => f.Contains('и'));
foreach (var fruit in withI)
Console.WriteLine(fruit); // "奇異果"
3. 陣列搜尋:Array 類別的方法
陣列本身沒有 Find(不像 List<T>),但有個靜態類別 Array 可以用:
int[] numbers = { 1, 2, 3, 2, 4 };
int pos = Array.IndexOf(numbers, 2); // 1 — 第一個 2 的索引
int lastPos = Array.LastIndexOf(numbers, 2); // 3 — 最後一個 2 的索引
Console.WriteLine(pos + ", " + lastPos);
如果你想根據條件找,可以用簡單的 for 迴圈:
int firstGreaterThanTwo = -1;
for (int i = 0; i < numbers.Length; i++)
{
if (numbers[i] > 2)
{
firstGreaterThanTwo = numbers[i];
break;
}
}
Console.WriteLine(firstGreaterThanTwo); // 3
4. 所有集合都能用的搜尋方法
很多集合都有搜尋方法(像 Contains、IndexOf、Find 等等)。如果要更複雜的,就自己寫個 for/foreach 迴圈。
範例:檢查有沒有水果名字是 "Б" 開頭
bool hasB = false;
foreach (var f in fruits)
{
if (f.StartsWith("Б"))
{
hasB = true;
break;
}
}
Console.WriteLine(hasB); // True
範例:找第一個名字裡有 "и" 的水果
string withI = null;
foreach (var f in fruits)
{
if (f.Contains('и'))
{
withI = f;
break;
}
}
Console.WriteLine(withI); // "奇異果"
範例:搜尋自訂物件
class Student
{
public string Name;
public int Group;
public int Id;
}
List<Student> students = new List<Student>
{
new Student { Name = "伊凡", Group = 101, Id = 1 },
new Student { Name = "瑪麗亞", Group = 101, Id = 2 },
new Student { Name = "彼得", Group = 102, Id = 3 },
};
// 找 Id == 2 的學生
Student maria = null;
foreach (var s in students)
{
if (s.Id == 2)
{
maria = s;
break;
}
}
if (maria != null)
Console.WriteLine(maria.Name); // "瑪麗亞"
else
Console.WriteLine("找不到學生");
5. HashSet<T> 搜尋:只有「有沒有?」
HashSet(HashSet<T>)就是為了超快檢查「有沒有這個東西」而生的。不能用索引找,但檢查有沒有超快:
HashSet<int> set = new HashSet<int> { 1, 3, 5, 7 };
bool hasThree = set.Contains(3); // True
Console.WriteLine(hasThree);
// 如果你想根據條件找(比如有沒有偶數):
bool hasEven = false;
foreach (var x in set)
{
if (x % 2 == 0)
{
hasEven = true;
break;
}
}
Console.WriteLine(hasEven); // False
6. Dictionary 搜尋:Dictionary<TKey, TValue>
Dictionary 就是 key-value 配對的集合。用 key 搜尋超強。
檢查有沒有這個 key
Dictionary<int, string> idToName = new Dictionary<int, string>
{
{ 1, "瓦夏" }, { 2, "卡佳" }
};
if (idToName.ContainsKey(2))
Console.WriteLine(idToName[2]); // "卡佳"
用 key 找 value:安全寫法!
if (idToName.TryGetValue(3, out string result))
Console.WriteLine(result);
else
Console.WriteLine("沒有這個 Id 的學生"); // 會印這個
用 value 搜尋(很少用而且慢):
bool containsVasya = idToName.ContainsValue("瓦夏");
Console.WriteLine(containsVasya); // True
根據 key 或 value 條件找紀錄
// 第一個名字是 "К" 開頭的 Id
KeyValuePair<int, string> entry = default;
bool found = false;
foreach (var pair in idToName)
{
if (pair.Value.StartsWith("К"))
{
entry = pair;
found = true;
break;
}
}
if (found)
Console.WriteLine($"{entry.Key}: {entry.Value}"); // "2: 卡佳"
7. 有用的小細節
搜尋方法表
| 集合型別 | 檢查有沒有 Contains | 找索引 IndexOf | 根據條件找 Find | 安全用 key 找 TryGetValue |
|---|---|---|---|---|
|
有 | 有 | 有() |
- |
(陣列) |
有(/for 迴圈) |
有() |
for 迴圈 | - |
|
有 | - | 沒有(只能自己寫) | - |
|
有() |
- | 自己寫 key/value 搜尋 | 有 |
自己寫 for 迴圈搜尋
來練習一下,自己寫個搜尋方法。比如找第一個大於某個值的元素索引:
static int FindFirstGreaterIndex(IEnumerable<int> collection, int minValue)
{
int index = 0;
foreach (var item in collection)
{
if (item > minValue)
return index;
index++;
}
return -1; // 沒找到
}
var nums = new List<int> { 1, 4, 7, 2 };
Console.WriteLine(FindFirstGreaterIndex(nums, 3)); // 1(數字 4)
這樣就不管你是 List<T>、int[],甚至 HashSet<T> 都能用。
搜尋在實務上的應用
- 根據登入帳號或 email 在資料庫找使用者。
- 檢查購物車裡有沒有這個商品。
- 根據參數名稱快速找設定。
- 檢查某個步驟是不是已經做過(像 workflow)。
- 在 log 陣列裡找錯誤的位置。
大部分面試問到 middle 工程師都會問:「怎麼在集合裡找元素?你會怎麼寫一個回傳第一個/全部/索引的搜尋方法?」。所以搜尋真的很重要!
8. 搜尋常見錯誤跟小細節
很重要:搜尋方法會根據物件怎麼比較來決定結果。比如你 List 裡放自訂 class,預設比較是比 reference!如果你想「比內容」,要自己 override Equals 跟 GetHashCode(這個之後會講)。
問題範例:
var s1 = new Student { Name = "葉戈爾", Id = 42 };
students.Add(s1);
// 現在建立一個一模一樣的新物件:
var s2 = new Student { Name = "葉戈爾", Id = 42 };
Console.WriteLine(students.Contains(s2)); // False(!)
編譯器不是比欄位,是比 reference(這是不同的物件)。
GO TO FULL VERSION