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

Bru*_*ira 226 c# comparison list equals

又一个列表比较问题.

List<MyType> list1;
List<MyType> list2;
Run Code Online (Sandbox Code Playgroud)

我需要检查它们是否具有相同的元素,无论它们在列表中的位置如何.每个MyType对象可能在列表中出现多次.是否有内置函数可以检查这个?如果我保证每个元素只在列表中出现一次怎么办?

编辑:伙计们感谢答案,但我忘了添加一些东西,每个元素的出现次数在两个列表上都应该相同.

Guf*_*ffa 284

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

Enumerable.SequenceEqual(list1.OrderBy(t => t), list2.OrderBy(t => t))
Run Code Online (Sandbox Code Playgroud)

编辑:

这是一个执行得更好(大约快十倍)的解决方案,只需要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);
}
Run Code Online (Sandbox Code Playgroud)

编辑2:

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

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

  • 这是一个很好的答案,我认为它是正确的,它比我的短.我唯一的建议是使用`SequenceEqual`作为扩展方法.我还应该指出,这要求`T`是`IComparable`,而`ToLookup`版本只需要一个正确的`GetHashCode`和`Equals`实现. (3认同)
  • 我知道这是旧的,但是即使您添加了 IEqualityComparer 参数,如果您尝试迭代空列表,您也会得到空引用异常。您可以将以下内容添加到函数 `if (list1 == null &amp;&amp; list2 == null) { return true; 的开头 } if (list1 == null || list2 == null) { return false; }` (3认同)
  • 感谢代码,但使用字典只有一个问题 - 如果T是可空类型,传入集合中的null将导致它倒下.此外,也许可以将方法重命名为ScrambledEggs,因为我第一次看到它时就是这样看的.一定饿了...... (2认同)

LBu*_*kin 46

如上所述,这个问题很暧昧.该声明:

......他们都有相同的元素,无论他们在列表中的位置如何.每个MyType对象可能在列表中出现多次.

并不表示您是否要确保两个列表具有相同的对象或相同的不同集.

如果您想确保集合具有完全相同的成员集,而不管订单如何,您可以使用:

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

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

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

使用集合操作(Intersect,Union,Except)比使用类似的方法更有效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(); 
Run Code Online (Sandbox Code Playgroud)

  • 第一种方法在以下情况下不起作用:`a = new [] {1,5,5}`和`b = new [] {1,1,5}`.集合没有_exactly_同一组成员,但`areEquivalent`设置为`true`. (27认同)
  • @Remko Jansen是对的,方法是!a.除了(b).Any()是**BUGGY** - 它会说a = {2,2}和b = {1,2}是相等的.我想知道如何投票这么多? (2认同)

rec*_*ive 40

如果你不关心事件的数量,我会像这样接近它.使用哈希集将比简单迭代提供更好的性能.

var set1 = new HashSet<MyType>(list1);
var set2 = new HashSet<MyType>(list2);
return set1.SetEquals(set2);
Run Code Online (Sandbox Code Playgroud)

这需要你已经覆盖.GetHashCode()和实施IEquatable<MyType>MyType.

  • @recursive:是的,但答案只提到`GetHashCode`,好像不需要`Equals`. (3认同)
  • `HashSet&lt;T&gt;` 也需要你实现 `Equals` 方法。这些项目相互比较,不仅仅是比较的哈希码。 (2认同)
  • 你不需要 set2 成为哈希集..任何可枚举都可以 (2认同)

Tho*_*ken 12

除了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;          
}
Run Code Online (Sandbox Code Playgroud)

  • 这有效,但是如果你想让它尽可能地运行,你可能想要使用return!(list1.Except(list2).Any())&&!(list2.Except(list1).Any()); (#EDIT:该死,我不能让它正常工作.诅咒我的新生.) (2认同)

Bri*_*sio 8

认为这应该做你想要的:

list1.All(item => list2.Contains(item)) &&
list2.All(item => list1.Contains(item));
Run Code Online (Sandbox Code Playgroud)

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

list1.All(item => list2.Contains(item)) &&
list1.Distinct().Count() == list1.Count &&
list1.Count == list2.Count
Run Code Online (Sandbox Code Playgroud)

  • 你仍然可以得到误报.考虑{1,2,2}和{1,1,2},它们包含相同的项目,具有相同的计数,但仍然不相等. (3认同)
  • 虽然技术上正确,但"Contains()"的行为可能会导致O(N <sup> 2 </ sup>)性能.如果项目数量很大,则设置操作(例外,相交,联盟)执行得更好. (2认同)

Ani*_*Ani 6

这是一个稍微困难的问题,我认为这个问题简化为:"测试两个列表是否是彼此的排列."

我相信其他人提供的解决方案仅表明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()); 
}
Run Code Online (Sandbox Code Playgroud)