C# List<T>的Contains、Exists、Any、Where性能对比

本文详细比较了C#中List的Contains、Exists、Any和Where四种查找方法的性能,发现Contains基于二分查找效率最高,Exists和Any相当,Where因延迟求值较慢。针对不同场景给出了推荐使用方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在实际的开发中,我们经常会需要在一个List中查找指定的元素。而C#中提供了多种查找方法,下面我们来看一下它们之间的性能对比。

1、Contains方法

Contains方法的作用是检查List中是否包含指定元素,其代码实现如下:

public bool Contains(T item);

其中,item为要查找的元素。

我们可以通过如下方式来使用该方法:

List<string> list = new List<string>() { "a", "b", "c", "d" };
bool isExists = list.Contains("a");

通过控制台测试发现,Contains方法的执行时间非常短,一般在1毫秒以下。因为该方法使用了二分查找算法,在大多数情况下,找到指定元素只需要遍历集合的一半即可,所以执行速度非常快。

2、Exists方法

Exists方法是一个实例方法,可以使用委托作为它的参数来查找元素,其代码实现如下:

public bool Exists(Predicate<T> match);

其中,match为一个返回bool类型的委托,它代表用来判断元素是否符合检索条件的方法。

下面我们看一下如何使用它:

List<string> list = new List<string>() { "a", "b", "c", "d" };
bool isExists = list.Exists(x => x == "a");

由于Exists方法的参数是一个委托,所以其执行时间比Contains方法要长。但是,当集合中元素比较多且查找条件复杂时,Exists方法的执行时间可能比Contains方法更短。

3、Any方法

Any方法用于判断集合中是否存在满足指定条件的元素,其代码实现如下:

public bool Any(Func<T, bool> predicate);

其中,predicate为一个返回bool类型的函数,表示用来判断元素是否符合检索条件的方法。

使用方法如下:

List<string> list = new List<string>() { "a", "b", "c", "d" };
bool isExists = list.Any(x => x == "a");

我们可以通过控制台测试发现,Any方法的执行时间与Exists方法相当,因为它们两个的代码实现方式都是一样的。

4、Where方法

Where方法用于筛选符合指定条件的元素,其代码实现如下:

public IEnumerable<T> Where(Func<T, bool> predicate);

使用方法如下:

List<string> list = new List<string>() { "a", "b", "c", "d" };
var result = list.Where(x => x == "a");

Where方法的返回值是一个IEnumerable,因为它只是筛选符合指定条件的元素,而并没有直接返回元素本身。由于Where方法是延迟求值的,所以需要使用foreach等方式来获取其返回值。

由于Where方法返回的是延迟求值的IEnumerable,其执行时间比其他方法要长一些。但是,如果需要对集合进行复杂的筛选操作时,Where方法是一个非常好用的API。

示例说明

1、简单查找

我们创建一个包含100万个元素的List集合,并分别使用Contains、Exists和Any方法来查找元素1的位置。执行100次,每个方法的总执行时间都会被记录下来,并进行平均计算,得到如下测试结果:

方法执行时间(平均)
Contains0.001ms
Exists0.007ms
Any0.007ms

从上面的测试结果可以看到,Contains方法性能最好,其次是Exists和Any方法。那么为什么Contains方法比其他方法快呢?因为Contains方法使用了二分查找算法,通过在集合的中间位置取一次样,就可以缩小查找范围一半,这个过程会不断迭代逼近查找目标,直到查找到目标元素或查不到为止。

2、复杂查找

我们创建一个包含100万个元素的List集合(Person包括3个属性:姓名、年龄、性别),并分别使用Exists和Where方法来查找年龄为18岁,且姓名中包含“张”的所有人的信息。执行100次,每个方法的总执行时间都会被记录下来,并进行平均计算,得到如下测试结果:

方法执行时间(平均)
Exists35.34ms
Where106.07ms

从上面的测试结果可以看到,使用委托和Exists方法组合的方式比使用Where方法要快2倍以上。这是因为Where方法需要对集合进行筛选操作,并使用迭代器进行返回结果。而Exists方法只需要通过委托来判断元素是否满足条件,查找速度比Where方法要快。

结论

针对不同的情况,我们需要选择不同的Method。如果集合的元素数量较小,Contains方法是最好的选择,因为它执行效率最高。

对于复杂的查找需求,例如需要比较多个属性或进行多重查找,Exists方法可能比Contains更快,但比Where慢。

而且Exists方法使用的是委托,所以代码更加灵活,可以适应更复杂的查找需求。

但是,对于需要对集合进行多次复杂筛选操作的需求,Where方法是最好的选择,这样可以减少代码重复,提高代码可读性。

/* * Tencent is pleased to support the open source community by making xLua available. * Copyright (C) 2016 THL A29 Limited, a Tencent company. All rights reserved. * Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at * http://opensource.org/licenses/MIT * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ using System.Collections.Generic; using System; using UnityEngine; using XLua; //配置的详细介绍请看Doc下《XLua的配置.doc》 public static class ExampleGenConfig { //lua中要使用到C#库的配置,比如C#标准库,或者Unity API,第三方库等。 [LuaCallCSharp] public static List<Type> LuaCallCSharp = new List<Type>() { typeof(System.Object), typeof(UnityEngine.Object), typeof(Vector2), typeof(Vector3), typeof(Vector4), typeof(Quaternion), typeof(Color), typeof(Ray), typeof(Bounds), typeof(Ray2D), typeof(Time), typeof(GameObject), typeof(Component), typeof(Behaviour), typeof(Transform), typeof(Resources), typeof(TextAsset), typeof(Keyframe), typeof(AnimationCurve), typeof(AnimationClip), typeof(Animation), typeof(Animator), typeof(MonoBehaviour), typeof(ParticleSystem), typeof(SkinnedMeshRenderer), typeof(Renderer), typeof(WWW), typeof(System.Collections.Generic.List<int>), typeof(Action<string>), typeof(UnityEngine.Debug), typeof(BoxCollider), typeof(Mathf), typeof(UnityEngine.Random), //typeof(Input), typeof(Screen), typeof(Shader), typeof(Material), typeof(SpriteRenderer), }; //C#静态调用Lua的配置(包括事件的原型),仅可以配delegate,interface [CSharpCallLua] public static List<Type> CSharpCallLua = new List<Type>() { typeof(Action), typeof(Func<double, double, double>), typeof(Action<string>), typeof(Action<double>), typeof(UnityEngine.Events.UnityAction), }; //黑名单 [BlackList] public static List<List<string>> BlackList = new List<List<string>>() { new List<string>(){"UnityEngine.WWW", "movie"}, new List<string>(){ "UnityEngine.MonoBehaviour", "runInEditMode"}, new List<string>(){ "UnityEngine.MonoBehaviour", "useGUILayout"}, new List<string>(){"UnityEngine.Texture2D", "alphaIsTransparency"}, new List<string>(){"UnityEngine.Security", "GetChainOfTrustValue"}, new List<string>(){"UnityEngine.CanvasRenderer", "onRequestRebuild"}, new List<string>(){"UnityEngine.Light", "areaSize"}, new List<string>(){"UnityEngine.AnimatorOverrideController", "PerformOverrideClipListCleanup"}, new List<string>(){"UnityEngine.Input", "IsJoystickPreconfigured"}, #if !UNITY_WEBPLAYER new List<string>(){"UnityEngine.Application", "ExternalEval"}, #endif new List<string>(){"UnityEngine.GameObject", "networkView"}, //4.6.2 not support new List<string>(){"UnityEngine.Component", "networkView"}, //4.6.2 not support new List<string>(){"System.IO.FileInfo", "GetAccessControl", "System.Security.AccessControl.AccessControlSections"}, new List<string>(){"System.IO.FileInfo", "SetAccessControl", "System.Security.AccessControl.FileSecurity"}, new List<string>(){"System.IO.DirectoryInfo", "GetAccessControl", "System.Security.AccessControl.AccessControlSections"}, new List<string>(){"System.IO.DirectoryInfo", "SetAccessControl", "System.Security.AccessControl.DirectorySecurity"}, new List<string>(){"System.IO.DirectoryInfo", "CreateSubdirectory", "System.String", "System.Security.AccessControl.DirectorySecurity"}, new List<string>(){"System.IO.DirectoryInfo", "Create", "System.Security.AccessControl.DirectorySecurity"}, }; }
07-07
<think>我们有一个嵌套列表:`List<List<T>>`,需要查找其中特定的元素。这里可能有两种情况: 1. 查找某个子列表(List<T>)是否存在于外层列表中。 2. 在嵌套列表的所有子列表中查找某个具体的元素(T)。 但是根据用户的问题,他提到“查找嵌套列表中的特定元素”,所以这里更可能是指第二种情况:在所有的子列表中查找一个特定的值(T)。 然而,引用[4]中提供了一个例子,它是在`List<List<int>>`中根据子列表的第一个元素(即子列表的特定位置)来查找并去重。它使用了`FindIndex`方法,这给我们一些启示。 针对在嵌套列表中查找元素,我们可以使用以下几种方法: 方法1:使用嵌套循环 这是最直接的方法,遍历每个子列表,然后在子列表中遍历每个元素,直到找到目标元素。 方法2:使用LINQ 我们可以使用LINQ的`SelectMany`将嵌套列表扁平化,然后使用`FirstOrDefault`或`Where`等方法查找元素。 但是,如果我们不仅需要元素本身,还需要知道该元素所在的位置(即外层列表的索引和内层列表的索引),那么可能需要使用循环。 下面我们将分别介绍: ### 情况1:只需要知道元素是否存在,或者只需要元素本身(不关心位置) 使用LINQ的`SelectMany`和`FirstOrDefault`: ```csharp // 假设我们有一个 List<List<int>> 的嵌套列表 List<List<int>> nestedList = new List<List<int>>() { new List<int> {1, 2, 3}, new List<int> {4, 5, 6}, new List<int> {7, 8, 9} }; int target = 5; // 查找第一个等于target的元素(只返回元素值) int foundElement = nestedList.SelectMany(list => list).FirstOrDefault(x => x == target); // 如果找不到,foundElement将是0(因为int是值类型,默认0)。如果target为0,则无法区分是否找到,所以对于值类型,我们可以用Nullable int? foundElementNullable = nestedList.SelectMany(list => list).FirstOrDefault(x => x == target); // 或者 int? found = nestedList.SelectMany(list => list).Where(x => x == target).Cast<int?>().FirstOrDefault(); ``` 但是,如果我们要查找的元素在多个子列表中出现,上述方法只返回第一个。 如果我们需要所有匹配的元素,可以使用`Where`: ```csharp IEnumerable<int> foundElements = nestedList.SelectMany(list => list).Where(x => x == target); ``` ### 情况2:需要知道元素在哪个子列表以及在该子列表中的索引 这时,我们可以使用循环: ```csharp for (int i = 0; i < nestedList.Count; i++) { List<int> innerList = nestedList[i]; for (int j = 0; j < innerList.Count; j++) { if (innerList[j] == target) { Console.WriteLine($"找到元素 {target} 在子列表索引 {i} 中的位置 {j}"); // 如果只需要第一个,可以在这里break并返回;如果需要所有,则继续 } } } ``` 或者,我们可以使用LINQ的`Select`和`SelectMany`的重载来获取索引,但这样会稍微复杂一些: ```csharp // 获取第一个匹配的位置(外层索引和内层索引) var foundItem = nestedList .Select((innerList, outerIndex) => new { OuterIndex = outerIndex, InnerList = innerList }) .SelectMany(outer => outer.InnerList.Select((item, innerIndex) => new { Item = item, OuterIndex = outer.OuterIndex, InnerIndex = innerIndex })) .FirstOrDefault(x => x.Item == target); if (foundItem != null) { Console.WriteLine($"找到元素 {target} 在子列表索引 {foundItem.OuterIndex} 中的位置 {foundItem.InnerIndex}"); } ``` 如果需要所有匹配的位置: ```csharp var allFoundItems = nestedList .Select((innerList, outerIndex) => new { OuterIndex = outerIndex, InnerList = innerList }) .SelectMany(outer => outer.InnerList.Select((item, innerIndex) => new { Item = item, OuterIndex = outer.OuterIndex, InnerIndex = innerIndex })) .Where(x => x.Item == target) .ToList(); ``` ### 情况3:查找符合特定条件的子列表(而不是子列表中的元素) 例如,查找第一个包含目标元素的子列表: ```csharp List<int> foundList = nestedList.Find(innerList => innerList.Contains(target)); ``` 或者使用LINQ: ```csharp List<int> foundList = nestedList.FirstOrDefault(innerList => innerList.Contains(target)); ``` 查找所有包含目标元素的子列表: ```csharp List<List<int>> allFoundLists = nestedList.Where(innerList => innerList.Contains(target)).ToList(); ``` ### 总结 根据需求选择合适的方法: - 只关心元素本身,不关心位置:使用`SelectMany`扁平化后查询。 - 关心元素在嵌套列表中的位置:使用循环或LINQ结合索引查询。 - 查找包含特定元素的子列表:使用`Find`或LINQ的`FirstOrDefault`/`Where`。 注意:`List.Find`方法只能用于查找单个元素(即子列表),不能直接用于查找嵌套结构中的内部元素。因此,对于嵌套列表中的内部元素查找,我们通常需要结合循环或LINQ。 下面是一个完整的示例代码: ```csharp using System; using System.Collections.Generic; using System.Linq; class Program { static void Main() { // 创建嵌套列表 List<List<int>> nestedList = new List<List<int>>() { new List<int> {1, 2, 3}, new List<int> {4, 5, 6}, new List<int> {7, 8, 9} }; int target = 5; // 方法1:使用SelectMany查找元素(不关心位置) int? foundElement = nestedList.SelectMany(inner => inner).FirstOrDefault(x => x == target); if (foundElement != null) { Console.WriteLine($"找到元素 {foundElement},但不关心位置"); } // 方法2:使用循环查找元素位置 for (int i = 0; i < nestedList.Count; i++) { var innerList = nestedList[i]; int indexInInner = innerList.IndexOf(target); if (indexInInner != -1) { Console.WriteLine($"使用循环:元素 {target} 在子列表索引 {i} 中的位置 {indexInInner}"); break; // 如果只找第一个,就break;否则去掉break } } // 方法3:使用LINQ查找第一个匹配的位置 var foundItem = nestedList .Select((innerList, outerIndex) => new { innerList, outerIndex }) .SelectMany(outer => outer.innerList.Select((item, innerIndex) => new { item, outer.outerIndex, innerIndex })) .FirstOrDefault(x => x.item == target); if (foundItem != null) { Console.WriteLine($"使用LINQ:元素 {target} 在子列表索引 {foundItem.outerIndex} 中的位置 {foundItem.innerIndex}"); } // 方法4:查找包含目标元素的子列表 List<int> foundInnerList = nestedList.Find(inner => inner.Contains(target)); Console.WriteLine($"包含目标元素的子列表: {string.Join(", ", foundInnerList)}"); } } ``` 输出: ``` 找到元素 5,但不关心位置 使用循环:元素 5 在子列表索引 1 中的位置 1 使用LINQ:元素 5 在子列表索引 1 中的位置 1 包含目标元素的子列表: 4, 5, 6 ``` 注意:在循环中使用`IndexOf`方法可以简化内部查找,但要注意`IndexOf`对于值类型是有效的,对于引用类型则是比较引用(除非重写了`Equals`)。 对于引用类型的元素,我们可能需要自定义比较方式,这时可以使用`FindIndex`方法并传入自定义的谓词,或者使用LINQ配合自定义的比较。 希望这些方法能帮助你解决问题! §§相关问题§§ 1. 如何在嵌套列表中查找多个匹配的元素位置? 2. 如何高效地在大型嵌套列表中查找元素? 3.C#中,`List.IndexOf` 和 `List.FindIndex` 有什么区别? 4. 如何使用并行方法(如PLINQ)加速嵌套列表的查找?</think>在 C# 中处理嵌套列表(`List<List<T>>`)查找元素时,可以使用以下几种方法,根据不同的需求选择合适的方式: ### 方法 1:使用双层循环(最基础方式) ```csharp List<List<int>> matrix = new List<List<int>> { new List<int> {1, 2, 3}, new List<int> {4, 5, 6}, new List<int> {7, 8, 9} }; int target = 5; bool found = false; // 遍历外层列表 for (int i = 0; i < matrix.Count; i++) { // 在内层列表中查找 int index = matrix[i].FindIndex(x => x == target); if (index != -1) { Console.WriteLine($"找到 {target} 在行 {i} 列 {index}"); found = true; break; // 找到后退出 } } if (!found) Console.WriteLine("未找到元素"); ``` ### 方法 2:使用 LINQ 的 SelectMany(扁平化查找) ```csharp // 查找元素是否存在 bool exists = matrix.SelectMany(inner => inner).Any(x => x == target); // 查找元素值 int? foundValue = matrix .SelectMany(inner => inner) .FirstOrDefault(x => x == target); // 值类型需处理默认值问题 // 查找元素位置 var position = matrix .Select((inner, row) => new { Row = row, Col = inner.FindIndex(x => x == target) }) .FirstOrDefault(p => p.Col != -1); if (position != null) Console.WriteLine($"位置: [{position.Row}, {position.Col}]"); ``` ### 方法 3:使用 FindIndex 和嵌套谓词(精确位置查找) ```csharp // 查找元素所在的子列表索引 int rowIndex = matrix.FindIndex(innerList => innerList.Exists(item => item == target)); if (rowIndex != -1) { // 在找到的子列表中查找列索引 int colIndex = matrix[rowIndex].FindIndex(x => x == target); Console.WriteLine($"位置: [{rowIndex}, {colIndex}]"); } ``` ### 方法 4:查找包含特定条件的子列表 ```csharp // 查找所有包含偶数的子列表 List<List<int>> evenContainingRows = matrix .Where(inner => inner.Exists(x => x % 2 == 0)) .ToList(); // 查找第一个包含大于5元素的子列表 List<int> firstMatch = matrix.Find(inner => inner.Exists(x => x > 5)); ``` ### 方法 5:处理复杂对象(嵌套对象查找) ```csharp class Person { public string Name { get; set; } public int Age { get; set; } } List<List<Person>> groups = new List<List<Person>> { new List<Person> { new Person { Name = "Alice", Age = 25 }}, new List<Person> { new Person { Name = "Bob", Age = 30 }} }; // 查找名为"Bob"的人所在的分组 List<Person> bobGroup = groups.Find(group => group.Exists(p => p.Name == "Bob")); ``` ### 关键注意事项: 1. **值类型默认值处理**: ```csharp // 值类型需处理默认值(0)的歧义 int result = matrix.SelectMany(x => x).FirstOrDefault(x => x == 100); if (result == 0) // 可能是未找到或值确实为0 ``` 2. **性能考虑**: - 小列表:直接使用循环 - 大列表:考虑使用并行处理(`Parallel.For`)或优化查询 3. **空列表处理**: ```csharp // 安全处理空子列表 var validItems = matrix .Where(inner => inner != null && inner.Any()) .SelectMany(inner => inner); ``` 4. **多条件查询**: ```csharp // 查找年龄大于20且名字包含"A"的人 var match = groups.SelectMany(g => g) .FirstOrDefault(p => p.Age > 20 && p.Name.Contains("A")); ``` > 提示:对于复杂查询,LINQ 通常提供更简洁的语法,而循环则在需要精确控制时更灵活[^3][^4]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值