1. 集合查找的基础能力
很多时候,“查找”和“过滤”这两个词好像差不多,但在编程里其实不一样。
- 过滤 —— 就是想拿到集合里所有满足某个条件的元素(比如所有大于10的数字)。
- 查找 —— 通常是想找某一个元素:第一个符合条件的、值等于某个值的,或者只是想知道集合里有没有它。
打个比方,像在图书馆:过滤——就是把所有关于“宇宙”的书都找出来,查找——就是问:“你们有《尤利西斯》这本书吗?”或者“第一本蓝色封皮的书在哪?”。
所有主流的.NET集合(数组、list、set、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(没有就返回-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:
// 名字里有“i”的所有水果
List<string> withI = fruits.FindAll(f => f.Contains('i'));
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等)。如果要更复杂的查找,就自己写循环遍历。
例子:判断有没有以“B”开头的水果
bool hasB = false;
foreach (var f in fruits)
{
if (f.StartsWith("B"))
{
hasB = true;
break;
}
}
Console.WriteLine(hasB); // True
例子:找第一个包含“i”的水果
string withI = null;
foreach (var f in fruits)
{
if (f.Contains('i'))
{
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>里查找:只有“有没有?”
Set(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
按value或key的条件查找记录
// 第一个名字以“K”开头的Id
KeyValuePair<int, string> entry = default;
bool found = false;
foreach (var pair in idToName)
{
if (pair.Value.StartsWith("K"))
{
entry = pair;
found = true;
break;
}
}
if (found)
Console.WriteLine($"{entry.Key}: {entry.Value}"); // "2: 卡佳"
7. 有用的小细节
查找方法表
| 集合类型 | 判断是否存在 Contains | 查找索引 IndexOf | 按条件查找 Find | 安全按key查找 TryGetValue |
|---|---|---|---|---|
|
有 | 有 | 有() |
- |
(数组) |
有(/循环) |
有() |
循环 | - |
|
有 | - | 没有(只能手写) | - |
|
有() |
- | 手写按key/value查 | 有 |
手写查找:自己写循环
想更深入点,可以自己写查找方法。比如,找第一个大于某个值的元素的索引:
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>都行),也不管内部实现。
查找在实际项目里的用法
- 按登录名或邮箱查找用户(比如数据库里)。
- 判断购物车里有没有某个商品。
- 按参数名快速查找设置。
- 判断某个步骤是不是已经执行过(比如workflow里)。
- 在日志数组里查找错误的位置。
大多数面试问middle开发,都会问:“你怎么在集合里查找元素?怎么写个方法返回第一个/所有/索引,按条件查找?”。所以查找的练习真的很有用!
8. 查找时常见的坑和细节
重点:查找方法依赖对象的比较方式。比如你往list里加自定义类,默认比较是按引用!想让查找“按内容”生效,得重写Equals和GetHashCode(后面课会详细讲)。
问题例子:
var s1 = new Student { Name = "叶戈尔", Id = 42 };
students.Add(s1);
// 现在新建一个同样Id和名字的新对象:
var s2 = new Student { Name = "叶戈尔", Id = 42 };
Console.WriteLine(students.Contains(s2)); // False(!)
编译器不是按字段比,是按引用(这其实是两个对象)。
GO TO FULL VERSION