在一个列表中查找不在另一个列表中的项目[重复]

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 才能在 Reference 类型上进行这项工作,并仅检查一个属性以进行比较。但这并没有太大的不同 - RemoveAll() 和 Contains() 是在这种情况下避免 Linq 的关键。我会尽快更新我的答案... 已编辑,我确实必须更改它。更容易使用 .Find() 而不是 Contains 和 Comparer 等等。【参考方案3】:

更快的方法是:

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公开,否则它将不起作用!

【讨论】:

以上是关于在一个列表中查找不在另一个列表中的项目[重复]的主要内容,如果未能解决你的问题,请参考以下文章

在 Python 元组列表中查找重复项

使用 4 mb 查找不在 40 亿列表中的整数。但是4 MB是不够的[重复]

查找不在列表 A 但列表 B 中的人员的更快方法 [重复]

查找列表中不重复的项目数

编写一个字谜查找器(来自 txt 文件中的单词列表)[重复]

c# 在 LINQ 查询返回的列表中查找项目并将其值与列表中的另一个项目进行比较