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

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

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

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


当前回答

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

我认为其他人提供的解决方案只是说明这两个列表是否包含相同的唯一元素。例如,这是一个必要但不充分的测试 {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()); 
}

其他回答

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

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

编辑:

下面是一个执行得更好的解决方案(大约快10倍),并且只需要相等,而不是icomcomparable:

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

我用这个方法)

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

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

我认为其他人提供的解决方案只是说明这两个列表是否包含相同的唯一元素。例如,这是一个必要但不充分的测试 {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()); 
}

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

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

参考:MSDN - c#比较两个对象列表

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

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

这将需要你重写. gethashcode()并在MyType上实现IEquatable<MyType>。