有没有一种方法在C#中获取两组对象之间的差异?

31

我想要在C#中获取两组整数的差异。给定s1和s2,我想返回那些存在于s1中但不存在于s2中的整数。我可以这样做:

    List<int> s1 = new List<int>();
    List<int> s2 = new List<int>();

    foreach (int i in s1)
    {
        if (s1.Contains(i))
        {
            //
        }
        else
        {
            //
        }
    }

但我想知道有没有更简洁的实现方式。我想做这样一件事:

List<int> omitted = s1.Difference(s2);

不确定是否存在现有的方法或LINQ结构,是否有人能指出?谢谢。

6个回答

36

我认为你需要使用HashSet.Except,这样可以避免使用列表(Lists),而是使用哈希集合(HashSets),从而使操作变得更加方便。如果你所表示的内容确实是一个“集合”,那么哈希集合是更好的一种类型。(如果你已经有一个列表,你只需将其创建为“新哈希集合”即可。)


Except()是我正在寻找的方法,谢谢你的建议关于HashSets。非常感谢。 - SiC
除非是扩展函数,不然就是吗? - leppie
对于所有的IEnumerable,都有一个名为Enumerable.Except的扩展方法。但是HashSet具有专门针对HashSets的Except方法。 - Brian
Except确实是一个扩展方法,可用于任何IEnumerable(请参见http://msdn.microsoft.com/en-us/library/system.linq.enumerable_members.aspx)。这意味着它可以在List<T>上正常工作。当然,如果顺序不重要,HashSet /是/更合适的类型。 - Matthew Flaschen
9
Brian,我不这么认为。你提供的方法是扩展方法。HashSet有一个ExceptWith方法,可以从实例HashSet中破坏性地删除参数中的元素,但没有Except方法。 - Matthew Flaschen

33
IEnumerable<T> a, b;

var added = a.Except(b);
var removed = b.Except(a);

2
请注意,此代码使用了Enumerable.Except,因此您需要使用System.Linq。http://msdn.microsoft.com/en-us/library/bb300779.aspx - Brian
1
有问题吗?不,我只是想帮助那些可能会复制粘贴这段代码却发现它无法编译的人。 - Brian
3
@Brian,同样的注意事项也适用于您的回答。HashSet没有实现自己的Except方法,因此您的回答也需要使用System.Linq。 - LukeH
2
@Deviant:但是你的运行时间复杂度是O(n^2),而我的是O(n) :) - leppie
2
这个答案表明,你必须调用两次Except才能获取添加和删除的项目。这一点很容易被忽视。 - Nils Lande

5

2
List<int> s1 = new List<int>();
List<int> s2 = new List<int>();

return sl.FindAll( i => !s2.Contains(i) )

1
从 s1 中取出 x
其中不包含 s2 的 x
选择 x

0
这里有两个扩展方法,可能在需要查找两个IEnumerable之间的无序差异时会很有用(它与leppie的答案大致相同,包装成了扩展方法):
public class EnumerableDifferences<T>
{
    public IEnumerable<T> Added { get; }
    public IEnumerable<T> Removed { get; }

    public EnumerableDifferences(IEnumerable<T> added, IEnumerable<T> removed)
    {
        Added = added;
        Removed = removed;
    }
}

public static class EnumerableExtensions
{
    public static HashSet<TSource> ToHashSet<TSource>(this IEnumerable<TSource> source, IEqualityComparer<TSource> comparer)
    {
        return new HashSet<TSource>(source, comparer);
    }

    public static IEnumerable<TSource> ExceptBy<TSource, TKey>(this IEnumerable<TSource> first, IEnumerable<TSource> second, Func<TSource, TKey> keySelector, IEqualityComparer<TKey> keyComparer = null)
    {
        return first
            .ExceptBy(keySelector, second.Select(keySelector), keyComparer);
    }

    public static IEnumerable<TSource> ExceptBy<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector, IEnumerable<TKey> keys, IEqualityComparer<TKey> keyComparer = null)
    {
        var secondKeys = keys.ToHashSet(keyComparer);

        foreach (var firstItem in source)
        {
            var firstItemKey = keySelector(firstItem);

            if (!secondKeys.Contains(firstItemKey))
            {
                yield return firstItem;
            }
        }
    }

    public static EnumerableDifferences<TSource> DifferencesBy<TSource, TKey>(this IEnumerable<TSource> first, IEnumerable<TSource> second, Func<TSource, TKey> keySelector, IEqualityComparer<TKey> keyComparer = null)
    {
        keyComparer = keyComparer ?? EqualityComparer<TKey>.Default;

        var removed = first.ExceptBy(second, keySelector, keyComparer);
        var added = second.ExceptBy(first, keySelector, keyComparer);

        var result = new EnumerableDifferences<TSource>(added, removed);

        return result;
    }

    public static EnumerableDifferences<TSource> Differences<TSource>(this IEnumerable<TSource> first, IEnumerable<TSource> second, IEqualityComparer<TSource> comparer = null)
    {
        return first
            .DifferencesBy(second, x => x, comparer);
    }
}

public static class Program
{
    public static void Main(params string[] args)
    {
        var l1 = new[] { 'a', 'b', 'c' };
        var l2 = new[] { 'a', 'd', 'c' };

        var result = l1.Differences(l2);

        Console.ReadKey();
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接