关于c#:比较两个List<>的最快方法

Quickest way to compare two List<>

比较两个大型(>50000个项目)最快(也是最不消耗资源)的是什么,结果有两个列表,如下所示:

  • 显示在第一个列表中但不显示在第二个列表中的项
  • 显示在第二个列表中但不在第一个列表中的项
  • 目前,我正在使用list或ireadOnlyCollection,并在Linq查询中解决此问题:

    1
    2
    var list1 = list.Where(i => !list2.Contains(i)).ToList();
    var list2 = list2.Where(i => !list.Contains(i)).ToList();

    但这并没有我想的那么好。如果我需要处理大量的列表,有没有想过让这个过程更快、更少地占用资源?


    使用Except

    1
    2
    var firstNotSecond = list1.Except(list2).ToList();
    var secondNotFirst = list2.Except(list1).ToList();

    我怀疑有些方法实际上比这快一点,但即使这样也比O(N*M)方法快得多。

    如果要组合这些,可以使用上面的方法创建一个方法,然后创建一个RETURN语句:

    1
    return !firstNotSecond.Any() && !secondNotFirst.Any();


    更有效的方法是使用Enumerable.Except

    1
    2
    var inListButNotInList2 = list.Except(list2);
    var inList2ButNotInList = list2.Except(list);

    此方法是通过使用延迟执行来实现的。这意味着你可以写例如:

    1
    var first10 = inListButNotInList2.Take(10);

    它也很有效,因为它内部使用Set比较对象。它首先从第二个序列中收集所有不同的值,然后流式处理第一个序列的结果,检查它们以前是否见过。


    如果希望结果不区分大小写,则可以使用以下方法:

    1
    2
    3
    4
    5
    List<string> list1 = new List<string> {"a.dll","b1.dll" };
    List<string> list2 = new List<string> {"A.dll","b2.dll" };

    var firstNotSecond = list1.Except(list2, StringComparer.OrdinalIgnoreCase).ToList();
    var secondNotFirst = list2.Except(list1, StringComparer.OrdinalIgnoreCase).ToList();

    firstNotSecond将包含b1.dll

    secondNotFirst将包含b2.dll


    不是为了这个问题,但这里有一些代码可以比较列表中的相等与否!相同的对象:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    public class EquatableList<T> : List<T>, IEquatable<EquatableList<T>> where    T : IEquatable<T>

    /// <summary>
    /// True, if this contains element with equal property-values
    /// </summary>
    /// <param name="element">element of Type T</param>
    /// <returns>True, if this contains element</returns>
    public new Boolean Contains(T element)
    {
        return this.Any(t => t.Equals(element));
    }

    /// <summary>
    /// True, if list is equal to this
    /// </summary>
    /// <param name="list">list</param>
    /// <returns>True, if instance equals list</returns>
    public Boolean Equals(EquatableList<T> list)
    {
        if (list == null) return false;
        return this.All(list.Contains) && list.All(this.Contains);
    }


    尝试这种方式:

    1
    2
    var difList = list1.Where(a => !list2.Any(a1 => a1.id == a.id))
                .Union(list2.Where(a => !list1.Any(a1 => a1.id == a.id)));


    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    using System.Collections.Generic;
    using System.Linq;

    namespace YourProject.Extensions
    {
        public static class ListExtensions
        {
            public static bool SetwiseEquivalentTo<T>(this List<T> list, List<T> other)
                where T: IEquatable<T>
            {
                if (list.Except(other).Any())
                    return false;
                if (other.Except(list).Any())
                    return false;
                return true;
            }
        }
    }

    有时,您只需要知道两个列表是否不同,而不需要知道这些差异是什么。在这种情况下,考虑将这个扩展方法添加到项目中。请注意,列出的对象应该实现IEquatable!

    用途:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    public sealed class Car : IEquatable<Car>
    {
        public Price Price { get; }
        public List<Component> Components { get; }

        ...
        public override bool Equals(object obj)
            => obj is Car other && Equals(other);

        public bool Equals(Car other)
            => Price == other.Price
                && Components.SetwiseEquivalentTo(other.Components);

        public override int GetHashCode()
            => Components.Aggregate(
                Price.GetHashCode(),
                (code, next) => code ^ next.GetHashCode()); // Bitwise XOR
    }

    无论Component类是什么,这里为Car显示的方法应该几乎相同地实现。

    注意我们如何编写gethashcode是非常重要的。为了正确实现IEquatableEqualsGetHashCode必须以逻辑上兼容的方式对实例的属性进行操作。

    两个内容相同的列表仍然是不同的对象,将产生不同的哈希代码。既然我们希望这两个列表被平等对待,我们必须让GetHashCode为每个列表产生相同的值。我们可以通过将哈希代码委托给列表中的每一个元素,并使用标准的位异或将它们组合在一起来实现这一点。XOR是顺序不可知论者,所以列表的排序是否不同并不重要。重要的是它们只包含等价的成员。

    注意:这个奇怪的名称意味着这个方法不考虑列表中元素的顺序。如果您关心列表中元素的顺序,那么这个方法不适合您!


    我用这段代码比较了两个拥有数百万条记录的列表。

    这个方法不会花很多时间

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
        //Method to compare two list of string
        private List<string> Contains(List<string> list1, List<string> list2)
        {
            List<string> result = new List<string>();

            result.AddRange(list1.Except(list2, StringComparer.OrdinalIgnoreCase));
            result.AddRange(list2.Except(list1, StringComparer.OrdinalIgnoreCase));

            return result;
        }

    如果只需要组合结果,这也会起作用:

    1
    2
    3
    var set1 = new HashSet<T>(list1);
    var set2 = new HashSet<T>(list2);
    var areEqual = set1.SetEquals(set2);

    其中t是lists元素的类型。


    可能很有趣,但对我有用

    string.join(",list1)!=string.join(",list2)


    我认为这是一种简单易行的方法来比较两个列表元素

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    x=[1,2,3,5,4,8,7,11,12,45,96,25]
    y=[2,4,5,6,8,7,88,9,6,55,44,23]

    tmp = []


    for i in range(len(x)) and range(len(y)):
        if x[i]>y[i]:
            tmp.append(1)
        else:
            tmp.append(0)
    print(tmp)


    这是你能找到的最好的解决办法

    1
    var list3 = list1.Where(l => list2.ToList().Contains(l));