关于c#:比较两个List 是否相等

Comparing two List for equality

除了一步一步地处理这些元素之外,如何比较两个相等的字符串列表(在.NET 3.0中):

这将失败:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// Expected result.
List<string> expected = new List<string>();
expected.Add("a" );
expected.Add("b" );
expected.Add("c" );

// Actual result
actual = new List<string>();
actual.Add("a" );
actual.Add("b" );
actual.Add("c" );

// Verdict
Assert.IsTrue( actual == expected );


尝试以下

1
var equal = expected.SequenceEqual(actual);

测试版本

1
Assert.IsTrue( actual.SequenceEqual(expected) );

SequenceEqual扩展方法将比较集合中的元素,以确保相等。

参见http://msdn.microsoft.com/zh-cn/library/bb348567(v=vs.100).aspx


许多测试框架都提供CollectionAssert类:

1
CollectionAssert.AreEqual(expected, actual);

例如MS测试


您总是可以自己编写所需的函数:

1
2
3
4
5
6
7
8
public static bool ListEquals< T >(IList< T > list1, IList< T > list2) {
    if (list1.Count != list2.Count)
        return false;
    for (int i = 0; i < list1.Count; i++)
        if (!list1[i].Equals(list2[i]))
            return false;
    return true;
}

并使用它:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// Expected result.
List<string> expected = new List<string>();
expected.Add("a" );
expected.Add("b" );
expected.Add("c" );

// Actual result
actual = new List<string>();
actual.Add("a" );
actual.Add("b" );
actual.Add("c" );

// Verdict
Assert.IsTrue( ListEquals(actual, expected) );


我注意到没有人真正告诉过您为什么原始代码无法正常工作。这是因为==运算符通常会测试相等性(即两个实例都指向内存中的同一对象),除非该运算符已被重载。 List< T >没有定义==运算符,因此使用基本引用等于实现。

正如其他张贴者所展示的那样,您通常必须逐步通过一些要素来测试"集合相等性"。当然,您应该使用DreamWalker用户建议的优化方法,该优化程序在逐步检查集合之前先测试集合的计数。


如果订单很重要:

1
bool equal = a.SequenceEquals(b);

如果顺序无关紧要:

1
bool equal = a.Count == b.Count && new HashSet<string>(a).SetEquals(b);

使用Linq并将代码编写为扩展方法:

1
2
3
4
5
6
public static bool EqualsOtherList< T >(this List< T > thisList, List< T > theOtherList)
{
  if (thisList == null || theOtherList == null ||
      thisList.Count != theOtherList.Count) return false;
  return !thisList.Where((t, i) => !t.Equals(theOtherList[i])).Any();
}


您可以这样编写扩展方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public static class ListExtensions
    {
        public static bool IsEqual< T >(this IList< T > list,IList< T > target, IComparer< T > comparer) where T:IComparable< T >
        {
            if (list.Count != target.Count)
            {
                return false;
            }
            int index = 0;
            while (index < list.Count &&
                   comparer.Compare(list[index],target[index]) == 0)
            {
                index++;
            }
            if (index != list.Count)
            {
                return false;
            }
            return true;
        }
    }

并这样称呼它:

1
2
3
List<int> intList = new List<int> { 1, 234, 2, 324, 324, 2 };
List<int> targetList = new List<int> { 1, 234, 2, 324, 324 };
bool isEqual = intList.IsEqual(targetList, Comparer<int>.Default);

编辑:更新代码以改为使用静态方法,因为OP使用的是.NET 3.0

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public static bool IsEqual< T >(IList< T > sourceList, IList< T > targetList, IComparer< T > comparer) where T : IComparable< T >
        {
            if (sourceList.Count != targetList.Count)
            {
                return false;
            }
            int index = 0;
            while (index < sourceList.Count &&
                   comparer.Compare(sourceList[index], targetList[index]) == 0)
            {
                index++;
            }
            if (index != sourceList.Count)
            {
                return false;
            }
            return true;
        }

客户:

1
        bool isEqual = IsEqual(intList,targetList, Comparer<int>.Default);


可能以非常规方式使用它,但没有为自定义类型实现IEquatable

1
JsonConvert.SerializeObject( myList1) == JsonConvert.SerializeObject( myList2)

但通常情况下,您可以使用注释https://docs.microsoft.com/zh-cn/dotnet/api/system.linq.enumerable.sequenceequal?view=netframework-4.8中提到的SequenceEqual

另外,不要忘记为自定义类型实现IEquatable接口(对于字符串类型或其他结构不是必需的)


尽管它确实遍历集合,但是我创建的此扩展方法不需要两个列表的顺序相同,并且只要覆盖Equals方法,它也可以用于复杂类型。

以下两个列表将返回true:

1
2
3
4
5
6
7
8
9
10
11
12
13
List<string> list1 = new List<string>
{
    {"bob" },
    {"sally" },
    {"john" }
};

List<string> list2 = new List<string>
{
    {"sally" },
    {"john" },
    {"bob" }
};

方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
public static bool IsEqualTo< T >(this IList< T > list1, IList< T > list2)
{
    if (list1.Count != list2.Count)
    {
        return false;
    }

    List< T > list3 = new List< T >();

    foreach (var item in list2)
    {
        list3.Add(item);
    }

    foreach (var item in list1)
    {
        int index = -1;
        for (int x = 0; x < list3.Count; x++)
        {
            if (list3[x].Equals(item))
            {
                index = x;
            }
        }

        if (index > -1)
        {
            list3.RemoveAt(index);
        }
        else
        {
            return false;
        }
    }

    return !list3.Any();
}