我刚刚发现Except()
将从第一个列表中删除第二个列表中的所有元素,但它也会使得返回结果中的所有元素都不同。
我正在使用的简单方法是Where(v => !secondList.Contains(v))
任何人都可以向我解释为什么这是行为,并且如果可能的话,请指出解释此问题的文档?
答案 0 :(得分:24)
Except
函数的文档说明:
使用默认的相等比较器来比较值,生成两个序列的集合差异。
两组的设定差异被定义为第一组中未出现在第二组中的成员。
这里的重要词是设置,defined为:
...一个抽象的数据结构,可以存储某些值,没有任何特定的顺序,也没有重复的值......
由于Except
被记录为基于集合的操作,因此它还具有使结果值不同的效果。
答案 1 :(得分:2)
给出A = [1, 2, 2, 3, 3, 3]
和B = [3]
。
A.Except(B);
返回[1, 2]
,如his response的Greg Beech所述A.ExceptAll(B);
,返回[1, 2, 2, 3, 3]
(我发现名字不明确)。A.Where(v => !B.Contains(v))
来自OP的变通回报[1, 2, 2]
我认为OP变通是您想要的行为,而这一行为不予理treated。
OP解决的主要问题是List<T>.Contains(T)
是O(n)
,而Where
也是O(n)
使得解决方案O(n²)
及时(对于A和大小相等的B)和O(1)
在内存中。
通过使用哈希集,我们可以使其在时间O(n)
和在存储器中O(n)
:
// I accept any better name for this method
public static IEnumerable<TSource> ExceptFrom<TSource>(
IEnumerable<TSource> first,
IEnumerable<TSource> second,
IEqualityComparer<TSource> comparer)
{
if (first == null)
throw new ArgumentNullException(nameof(first));
if (second == null)
throw new ArgumentNullException(nameof(second));
var secondSet = second as HashSet<TSource> ?? // this trick ignore the comparer
second.ToHashSet(comparer ?? EqualityComparer<TSource>.Default);
// Contains is O(1) for HashSet.
return first.Where(v => !secondSet.Contains(v));
}
答案 2 :(得分:0)
您写道:
我正在使用的简单方法是
Where(v => !secondList.Contains(v))
执行此操作时,仍然会使用secondList
完成Distict。
例如:
var firstStrings = new [] { "1", null, null, null, "3", "3" };
var secondStrings = new [] { "1", "1", "1", null, null, "4" };
var resultStrings = firstStrings.Where(v => !secondStrings.Contains(v)); // 3, 3
我创建了一个没有任何区别的扩展方法。用法考试:
var result2Strings = firstStrings.ExceptAll(secondStrings).ToList(); // null, 3, 3
这就是它的作用:
这是来源:
public static IEnumerable<TSource> ExceptAll<TSource>(
this IEnumerable<TSource> first,
IEnumerable<TSource> second)
{
// Do not call reuse the overload method because that is a slower imlementation
if (first == null) { throw new ArgumentNullException("first"); }
if (second == null) { throw new ArgumentNullException("second"); }
var secondList = second.ToList();
return first.Where(s => !secondList.Remove(s));
}
public static IEnumerable<TSource> ExceptAll<TSource>(
this IEnumerable<TSource> first,
IEnumerable<TSource> second,
IEqualityComparer<TSource> comparer)
{
if (first == null) { throw new ArgumentNullException("first"); }
if (second == null) { throw new ArgumentNullException("second"); }
var comparerUsed = comparer ?? EqualityComparer<TSource>.Default;
var secondList = second.ToList();
foreach (var item in first)
{
if (secondList.Contains(item, comparerUsed))
{
secondList.Remove(item);
}
else
{
yield return item;
}
}
}
编辑:基于DigEmAll评论的更快的实现
public static IEnumerable<TSource> ExceptAll<TSource>(
this IEnumerable<TSource> first,
IEnumerable<TSource> second)
{
return ExceptAll(first, second, null);
}
public static IEnumerable<TSource> ExceptAll<TSource>(
this IEnumerable<TSource> first,
IEnumerable<TSource> second,
IEqualityComparer<TSource> comparer)
{
if (first == null) { throw new ArgumentNullException("first"); }
if (second == null) { throw new ArgumentNullException("second"); }
var secondCounts = new Dictionary<TSource, int>(comparer ?? EqualityComparer<TSource>.Default);
int count;
int nullCount = 0;
// Count the values from second
foreach (var item in second)
{
if (item == null)
{
nullCount++;
}
else
{
if (secondCounts.TryGetValue(item, out count))
{
secondCounts[item] = count + 1;
}
else
{
secondCounts.Add(item, 1);
}
}
}
// Yield the values from first
foreach (var item in first)
{
if (item == null)
{
nullCount--;
if (nullCount < 0)
{
yield return item;
}
}
else
{
if (secondCounts.TryGetValue(item, out count))
{
if (count == 0)
{
secondCounts.Remove(item);
yield return item;
}
else
{
secondCounts[item] = count - 1;
}
}
else
{
yield return item;
}
}
}
}
我的博客上的More info(也是Intersect和Union的变体)