在C#</t>中检查两个List <t>列表是否相等的最佳方法是什么

时间:2009-05-18 06:54:15

标签: c# linq list equality

有很多方法可以做到这一点,但我觉得我错过了一个功能或其他什么。

显然List == List将使用Object.Equals()并返回false

如果列表中的每个元素都相等并且出现在相反列表中的相同位置,那么我认为它们是相等的。我正在使用值类型,但正确实现的Data对象应该以相同的方式工作(即,我不是在寻找浅复制列表,只是内部每个对象的是相同的)。

我尝试过搜索并且有类似的问题,但我的问题是每个元素都按照确切的顺序相等。

6 个答案:

答案 0 :(得分:40)

Enumerable.SequenceEqual<TSource>

MSDN

答案 1 :(得分:3)

邪恶的实施是

if (List1.Count == List2.Count)
{
   for(int i = 0; i < List1.Count; i++)
   {
      if(List1[i] != List2[i])
      {
         return false;
      }
   }
   return true;
}
return false;

答案 2 :(得分:3)

我把这个变化放在一起:

private bool AreEqual<T>(List<T> x, List<T> y)
{
    // same list or both are null
    if (x == y)
    {
        return true;
    }

    // one is null (but not the other)
    if (x== null || y == null)
    {
        return false;
    }

    // count differs; they are not equal
    if (x.Count != y.Count)
    {
        return false;
    }

    for (int i = 0; i < x.Count; i++)
    {
        if (!x[i].Equals(y[i]))
        {
            return false;
        }
    }
    return true;
}

我的书呆子也爬了出来,所以我对SequenceEquals进行了性能测试,这个有一点点优势。

现在,要问的问题;这个微小的,几乎可测量的性能增益是否值得将代码添加到代码库并维护它?我非常怀疑它; o)

答案 3 :(得分:2)

我敲了一个快速扩展方法:

namespace ExtensionMethods
{
    public static class MyExtensions
    {
        public static bool Matches<T>(this List<T> list1, List<T> list2)
        {
            if (list1.Count != list2.Count) return false;
            for (var i = 0; i < list1.Count; i++)
            {
                if (list1[i] != list2[i]) return false;
            }
            return true;
        }
    }   
}

答案 4 :(得分:1)

可以为序列编写通用IEqualityComparer<T>。一个简单的:

public class SequenceEqualityComparer<T> : IEqualityComparer<IEnumerable<T>>
{
    public bool Equals(IEnumerable<T> x, IEnumerable<T> y)
    {
        return x.SequenceEqual(y);
    }

    public int GetHashCode(IEnumerable<T> obj)
    {
        return unchecked(obj.Aggregate(397, (x, y) => x * 31 + y.GetHashCode()));
    }
}

更加充实的版本:应该会有更好的表现。

public class SequenceEqualityComparer<T> : EqualityComparer<IEnumerable<T>>, 
                                           IEquatable<SequenceEqualityComparer<T>>
{
    readonly IEqualityComparer<T> comparer;

    public SequenceEqualityComparer(IEqualityComparer<T> comparer = null)
    {
        this.comparer = comparer ?? EqualityComparer<T>.Default;
    }

    public override bool Equals(IEnumerable<T> x, IEnumerable<T> y)
    {
        // safer to use ReferenceEquals as == could be overridden
        if (ReferenceEquals(x, y))
            return true;

        if (x == null || y == null)
            return false;

        var xICollection = x as ICollection<T>;
        if (xICollection != null)
        {
            var yICollection = y as ICollection<T>;
            if (yICollection != null)
            {
                if (xICollection.Count != yICollection.Count)
                    return false;

                var xIList = x as IList<T>;
                if (xIList != null)
                {
                    var yIList = y as IList<T>;
                    if (yIList != null)
                    {
                        // optimization - loops from bottom
                        for (int i = xIList.Count - 1; i >= 0; i--)
                            if (!comparer.Equals(xIList[i], yIList[i]))
                                return false;

                        return true;
                    }
                }
            }
        }

        return x.SequenceEqual(y, comparer);
    }

    public override int GetHashCode(IEnumerable<T> sequence)
    {
        unchecked
        {
            int hash = 397;
            foreach (var item in sequence)
                hash = hash * 31 + comparer.GetHashCode(item);

            return hash;
        }
    }

    public bool Equals(SequenceEqualityComparer<T> other)
    {
        if (ReferenceEquals(null, other))
            return false;

        if (ReferenceEquals(this, other))
            return true;

        return this.comparer.Equals(other.comparer);
    }

    public override bool Equals(object obj)
    {
        return Equals(obj as SequenceEqualityComparer<T>);
    }

    public override int GetHashCode()
    {
        return comparer.GetHashCode();
    }
}

这有一些功能:

  1. 比较从下到上进行。在典型的用例中,最终收集的可能性更大。

  2. 可以传递IEqualityComparer<T>作为集合中项目的比较。

答案 5 :(得分:0)

使用 linq SequenceEqual检查序列是否相等,因为Equals方法检查引用相等性。

bool isEqual = list1.SequenceEqual(list2);

SequenceEqual()方法将第二个I Enumerable<T>序列作为参数,并使用目标(第一个)序列执行逐个元素的比较。如果两个序列包含相同数量的元素,并且第一个序列中的每个元素等于第二个序列中的相应元素(使用默认相等比较器),那么SequenceEqual() returns true。否则,将返回false

或者,如果您不关心元素顺序,请使用Enumerable.All方法:

var isEqual = list1.All(list2.Contains);

第二个版本还需要对Count进行另一次检查,因为即使list2包含的元素多于list1,它也会返回true。