比较两个List<T>对象为相等,忽略顺序

又是一个比较列表的问题。

List<MyType> list1;
List<MyType> list2;

我需要检查它们是否具有相同的元素,而不管它们在列表中的位置。每个MyType对象可以在列表中出现多次。是否有一个内置函数来检查这个?如果我保证每个元素在列表中只出现一次呢?

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

296451 次浏览

认为这应该是你想要的:

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

如果你想要它是不同的,你可以把它改为:

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

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

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

这将要求你已经覆盖了.GetHashCode()并在MyType上实现了IEquatable<MyType>

这是一个有点难的问题,我认为可以简化为:“测试两个列表是否互为排列。”

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

我相信这应该是可行的,尽管它不是最有效的:

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());
}

如上所述,这个问题模棱两可。声明:

< p >…它们都有相同的元素,不管它们在列表中的位置如何。 每个MyType对象可以在列表中出现多次。< / p >

并没有指明你是否要确保这两个列表具有对象的同一组或相同的不同的设置

如果你想确保集合有完全相同的成员集,不管顺序如何,你可以使用:

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

如果你想确保两个集合有相同的不同的成员集(其中任何一个的重复都被忽略),你可以使用:

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

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

既然你已经澄清了你的问题,我可以说你想使用第一种形式——因为重复很重要。这里有一个简单的例子来证明你得到了你想要的结果:

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();

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

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

编辑:

下面是一个执行得稍微好一点的解决方案(大约快十倍),并且只需要IEquatable,而不是IComparable:

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指出的可空类型),您可以创建一个为字典接受比较器的版本:

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

除了Guffa的答案之外,您还可以使用这个变体来获得更简洁的符号。

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;
}

试试这个! !

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

// 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);

我用这个方法)

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);
}

这对我有用:
如果你正在比较依赖于ID这样的单个实体的两个对象列表,并且你想要第三个匹配该条件的列表,那么你可以执行以下操作:

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

参考:比较两个对象列表