在一个列表中查找不在另一个列表中的项目[重复]
Posted
技术标签:
【中文标题】在一个列表中查找不在另一个列表中的项目[重复]【英文标题】:Find items in one list that aren't in another list [duplicate] 【发布时间】:2020-01-08 16:00:48 【问题描述】:我有 2 个对象列表,我想从第一个对象中获取所有对象,其中字符串 a 与第二个列表中的字符串 a 不匹配。
public class ObjectA
string Item;
int b;
public class ObjectB
string Item;
int b;
使用 linq 可以通过这种方式轻松完成,但有什么更快的方法呢?
var newList = objectAList.Where(a => !objectBList.Any(b => b.Item == a.Item)).ToList()
【问题讨论】:
您的列表大小是多少?另一种方法是使用两个嵌套的 for 语句。但是说这会更快并没有说太多。你应该做一些基准测试。小尺寸列表的行为可能可以忽略不计。对于中型或大型可能不会。什么是小?什么是大?使用所有这些问题,我试图说服您,如果您没有找到解决相同问题的另一种方法然后执行基准测试,那么这没有任何意义。 您在寻找更快的方式、非 LINQ 方式还是同样更快的非 LINQ 方式?你有解决这个问题的想法或尝试吗? @Christos 嵌套的语句为 O(n*m)。 Linq 的 except 明显更好。 @EricJ。我同意。我的想法是,当您只有一种解决方案时,试图回答哪个更快是没有意义的。即使有两种解决方案,如果您不运行任何基准测试,任何猜测都可能毫无意义。 删除“无 LINQ”条件后,Most efficient way to compare two lists and delete the same 可能重复。与Find if listA contains any elements not in listB、Use LINQ to get items in one List<>, that are not in another List<> 或Difference between two lists 并没有什么不同。 【参考方案1】:Linq except 方法就是为此目的而设计的,而且速度非常快。但是,您有一个问题,即您的两个类具有兼容的字段,但它们是不同的对象。这是处理它的一种方法:
class ObjectBase
public string Item;
public int b;
class ObjectA : ObjectBase
class ObjectB : ObjectBase
class ObjectComparer : IEqualityComparer<ObjectBase>
public bool Equals(ObjectBase a, ObjectBase b)
return a?.Item == b?.Item;
public int GetHashCode(ObjectBase o)
return o.?Item?.GetHashCode() ?? 0;
// Very fast compared to your current approach. 1000x for my test case.
var newList = objectAList.Except(objectBList, new ObjectComparer()).ToList();
【讨论】:
【参考方案2】:这个怎么样 - 没有 linq,仍然很好和流利,为正确的类型编辑:
ObjectBList.RemoveAll(p => ObjectAList.Find(p2 => p2.Item == p.Item) != null ? true : false);
完整示例:
public class ObjectBase
public string Item;
public int b;
public class ObjectA : ObjectBase
public class ObjectB : ObjectBase
public List<ObjectB> Testing()
var list1 = new List<ObjectA> new ObjectA Item = "str1", b = 0 ;
var list2 = new List<ObjectB> new ObjectB Item = "str1", b = 0 , new ObjectB Item = "str2", b = 1 ;
// Key Line - Remove all from list2 found in list1
list2.RemoveAll(p => list1.Find(p2 => p2.Item == p.Item) != null ? true : false);
return list2;
【讨论】:
这不会作用于 OP 在他的问题中的对象。 是的,您必须使用带有 Contains 的 IEqualityComparer更快的方法是:
var newList = objectAList.Select(a => a.Item).Except(objectBList.Select(b => b.Item));
是的,我知道它是 Linq,但您要求更快的方法 :)
HTH
【讨论】:
除了是要走的路,但这只会给你项目,而不是实际的对象。【参考方案4】:你可以试试这个代码:
public static void Main(string[] args)
List<ObjectA> listA = new List<ObjectA>()
new ObjectA()Item = "abc" ,
new ObjectA()Item = "ab" ,
;
List<ObjectB> listB = new List<ObjectB>()
new ObjectB()Item = "abc" ,
;
// loop backwards removing entry if it is found in the other list
for (int i = listA.Count - 1; i >= 0; i--)
if (listB.Find(e => e.Item == listA[i].Item) != null)
listA.RemoveAt(i);
我将(你的和我的)两种方法都运行了五次,并在几毫秒内得到了以下结果(每次循环重复算法,100000 次迭代):
我的方法:107 46 94 67 91
您的方法:108 267 171 138 173
这也可能是由于额外的ToList()
调用并创建了新对象newList
。
所以,总而言之,如果有任何改进,那只是很小的一部分,我不会为此牺牲出色的 LINQ 方法提供的可读性。
此外,它们在内部被设计为尽可能快地工作,所以我会依赖它们:)
【讨论】:
RemoveAt 相当慢,因为它是一个 O(N) 操作,您必须执行 M 次(其中 M 是要删除的相交元素的数量),所以整体算法是 O(N* M)。【参考方案5】:时间上最快的方法是使用 HashSet,尤其是对于大型列表:
private List<ObjectA> Find(List<ObjectA> list1, List<ObjectB> list2)
var list2HashSet = list2.Select(x => x.Item).ToHashSet();
return list1.Where(x => !list2HashSet.Contains(x.Item)).ToList();
注意:并且不要忘记将属性Item
公开,否则它将不起作用!
【讨论】:
以上是关于在一个列表中查找不在另一个列表中的项目[重复]的主要内容,如果未能解决你的问题,请参考以下文章
使用 4 mb 查找不在 40 亿列表中的整数。但是4 MB是不够的[重复]