关于c#:比较两个List< T>

Compare two List<T> objects for equality, ignoring order

本问题已经有最佳答案,请猛点这里访问。

另一个比较问题的列表。

1
2
List<MyType> list1;
List<MyType> list2;

我需要检查它们是否都有相同的元素,不管它们在列表中的位置如何。每个MyType对象可能在列表中出现多次。是否有内置功能检查此项?如果我保证每个元素在一个列表中只出现一次呢?

编辑:伙计们,谢谢你们的回答,但我忘了添加一些东西,每个元素在两个列表中出现的次数应该是相同的。


如果您希望它们真正相等(即相同的项目和每个项目的相同数量),我认为最简单的解决方案是在比较之前进行排序:

1
Enumerable.SequenceEqual(list1.OrderBy(t => t), list2.OrderBy(t => t))

编辑:

这里有一个性能更好(大约快十倍)的解决方案,只需要IEquatable,而不是IComparable

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public static bool ScrambledEquals<T>(IEnumerable<T> list1, IEnumerable<T> list2) {
  var cnt = new Dictionary<T, int>();
  foreach (T s in list1) {
    if (cnt.ContainsKey(s)) {
      cnt[s]++;
    } else {
      cnt.Add(s, 1);
    }
  }
  foreach (T s in list2) {
    if (cnt.ContainsKey(s)) {
      cnt[s]--;
    } else {
      return false;
    }
  }
  return cnt.Values.All(c => c == 0);
}

编辑2:

要将任何数据类型作为键处理(例如Frank Tzanabetis指出的可以为空的类型),可以生成一个版本,该版本将字典使用比较器:

1
2
3
public static bool ScrambledEquals<T>(IEnumerable<T> list1, IEnumerable<T> list2, IEqualityComparer<T> comparer) {
  var cnt = new Dictionary<T, int>(comparer);
  ...


如前所述,这个问题是含糊不清的。声明:

... they both have the same elements, regardless of their position within the list.
Each MyType object may appear multiple times on a list.

不指示是否要确保两个列表具有相同的对象集或相同的互异集。

如果要确保集合具有完全相同的成员集(无论顺序如何),可以使用:

1
2
// lists should have same count of items, and set difference must be empty
var areEquivalent = (list1.Count == list2.Count) && !list1.Except(list2).Any();

如果要确保两个集合具有相同的不同成员集(其中两个集合中的重复项都被忽略),可以使用:

1
2
// check that [(A-B) Union (B-A)] is empty
var areEquivalent = !list1.Except(list2).Union( list2.Except(list1) ).Any();

使用set操作(IntersectUnionExcept比使用Contains等方法更有效。在我看来,它也更好地表达了您的查询的期望。

编辑:既然你已经澄清了你的问题,我可以说你想用第一个表格——因为重复很重要。下面是一个简单的例子,演示您获得想要的结果:

1
2
3
4
5
var a = new[] {1, 2, 3, 4, 4, 3, 1, 1, 2};
var b = new[] { 4, 3, 2, 3, 1, 1, 1, 4, 2 };

// result below should be true, since the two sets are equivalent...
var areEquivalent = (a.Count() == b.Count()) && !a.Except(b).Any();


如果你不关心发生的次数,我会这样处理。使用哈希集将比简单迭代提供更好的性能。

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

这将要求您覆盖.GetHashCode()并在MyType上实现IEquatable


除了Guffa的答案之外,您还可以使用这个变体来有一个更短小的符号。

1
2
3
4
5
6
public static bool ScrambledEquals<T>(this IEnumerable<T> list1, IEnumerable<T> list2)
{
  var deletedItems = list1.Except(list2).Any();
  var newItems = list2.Except(list1).Any();
  return !newItems && !deletedItems;          
}


认为这应该是你想要的:

1
2
list1.All(item => list2.Contains(item)) &&
list2.All(item => list1.Contains(item));

如果您希望它与众不同,可以将其更改为:

1
2
3
list1.All(item => list2.Contains(item)) &&
list1.Distinct().Count() == list1.Count &&
list1.Count == list2.Count


这是一个稍微困难的问题,我认为可以归结为:"测试两个列表是否是彼此的排列。"

我相信其他人提供的解决方案只表明这两个列表是否包含相同的唯一元素。例如,这是一个必要但不充分的测试{1, 1, 2, 3}不是{3, 3, 1, 2}的置换。尽管它们的计数是相等的,并且包含相同的不同元素。

尽管这不是最有效的方法,但我认为这是可行的:

1
2
3
4
5
6
7
8
9
10
11
static bool ArePermutations<T>(IList<T> list1, IList<T> list2)
{
   if(list1.Count != list2.Count)
         return false;

   var l1 = list1.ToLookup(t => t);
   var l2 = list2.ToLookup(t => t);

   return l1.Count == l2.Count
       && l1.All(group => l2.Contains(group.Key) && l2[group.Key].Count() == group.Count());
}


这对我很有用:如果要比较两个对象列表依赖于单个实体,如ID,并且需要第三个符合该条件的列表,则可以执行以下操作:

1
list3=List1.Where(n => !List2.select(n1 => n1.Id).Contains.(n.Id));

请参阅:msdn-c比较两个对象列表


我用这种方法)

1
2
3
4
5
6
7
public delegate bool CompareValue<in T1, in T2>(T1 val1, T2 val2);

public static bool CompareTwoArrays<T1, T2>(this IEnumerable<T1> array1, IEnumerable<T2> array2, CompareValue<T1, T2> compareValue)
{
    return array1.Select(item1 => array2.Any(item2 => compareValue(item1, item2))).All(search => search)
            && array2.Select(item2 => array1.Any(item1 => compareValue(item1, item2))).All(search => search);
}

回答

CountExceptAny这样结合起来。正如Bioukh所提到的,先到Count更快。

1
2
3
4
5
6
public static bool AreTheSameIgnoringOrder(List<string> x, List<string> y)
{
    return x.Count() == y.Count()
        && !x.Except(y).Any()
        && !y.Except(x).Any(); // re: Servy's comment.
}

演示

这是一把小提琴。

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

public class Program
{
    public static void Main()
    {
        var x = new List<string>() {"a","b","c"};

        var result1 = AreTheSameIgnoringOrder(x, new List<string>() {"c","b","a"});
        Console.WriteLine(result1);

        var result2 = AreTheSameIgnoringOrder(x, new List<string>() {"c","b","a","b" });
        Console.WriteLine(result2);
    }

    public static bool AreTheSameIgnoringOrder(List<string> x, List<string> y)
    {
        return x.Count() == y.Count()
            && !x.Except(y).Any()
            && !y.Except(x).Any();
    }
}


试试这个!!!!

使用以下代码,您可以根据需要比较一个或多个字段以生成结果列表。结果列表将只包含已修改的项。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// veriables been used
List<T> diffList = new List<T>();
List<T> gotResultList = new List<T>();



// compare First field within my MyList
gotResultList = MyList1.Where(a => !MyList2.Any(a1 => a1.MyListTField1 == a.MyListTField1)).ToList().Except(gotResultList.Where(a => !MyList2.Any(a1 => a1.MyListTField1 == a.MyListTField1))).ToList();
// Generate result list
diffList.AddRange(gotResultList);

// compare Second field within my MyList
gotResultList = MyList1.Where(a => !MyList2.Any(a1 => a1.MyListTField2 == a.MyListTField2)).ToList().Except(gotResultList.Where(a => !MyList2.Any(a1 => a1.MyListTField2 == a.MyListTField2))).ToList();
// Generate result list
diffList.AddRange(gotResultList);


MessageBox.Show(diffList.Count.ToString);