CodeGym /课程 /C# SELF /C#集合中查找元素

C#集合中查找元素

C# SELF
第 29 级 , 课程 1
可用

1. 集合查找的基础能力

很多时候,“查找”“过滤”这两个词好像差不多,但在编程里其实不一样。

  • 过滤 —— 就是想拿到集合里所有满足某个条件的元素(比如所有大于10的数字)。
  • 查找 —— 通常是想找某一个元素:第一个符合条件的、值等于某个值的,或者只是想知道集合里有没有它。

打个比方,像在图书馆:过滤——就是把所有关于“宇宙”的书都找出来,查找——就是问:“你们有《尤利西斯》这本书吗?”或者“第一本蓝色封皮的书在哪?”

所有主流的.NET集合(数组、list、set、dictionary等)都支持多种查找元素或判断是否存在的方法。

来看看最常用的方法:

集合 判断是否存在 查找索引 查找元素 按key查找
List<T>
Contains
IndexOf
Find
-
T[]
(数组)
Contains
/
Array.IndexOf
Array.IndexOf
- -
HashSet<T>
Contains
- - -
Dictionary<TKey,V>
ContainsKey
,
ContainsValue
- - 有索引器
[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. 所有集合通用的查找方法

很多集合都提供查找方法(比如ContainsIndexOfFind等)。如果要更复杂的查找,就自己写循环遍历。

例子:判断有没有以“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
List<T>
有(
Find
-
T[]
(数组)
有(
Array.Contains
/循环)
有(
Array.IndexOf
循环 -
HashSet<T>
- 没有(只能手写) -
Dictionary<TKey,V>
有(
ContainsKey
- 手写按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里加自定义类,默认比较是按引用!想让查找“按内容”生效,得重写EqualsGetHashCode(后面课会详细讲)。

问题例子:

var s1 = new Student { Name = "叶戈尔", Id = 42 };
students.Add(s1);
// 现在新建一个同样Id和名字的新对象:
var s2 = new Student { Name = "叶戈尔", Id = 42 };
Console.WriteLine(students.Contains(s2)); // False(!)

编译器不是按字段比,是按引用(这其实是两个对象)。

2
任务
C# SELF, 第 29 级, 课程 1
已锁定
在列表中查找第一个符合条件的元素
在列表中查找第一个符合条件的元素
评论
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION