代码之家  ›  专栏  ›  技术社区  ›  Stephan

除了有相似的效果不同吗?

  •  12
  • Stephan  · 技术社区  · 14 年前

    我刚发现 Except() 将从第一个列表中删除第二个列表中的所有元素,但其效果是使返回结果中的所有元素都不同。

    我使用的简单方法是 Where(v => !secondList.Contains(v))

    有人能给我解释一下为什么会有这种行为吗?如果可能的话,请给我指出解释这种行为的文档?

    2 回复  |  直到 14 年前
        1
  •  37
  •   Greg Beech    14 年前

    文件 Except 功能状态:

    通过使用默认的相等比较器来比较值,生成两个序列的集合差。

    两个集合的集合差定义为第一个集合的成员不出现在第二个集合中。

    这里最重要的是 设置 defined

    …一种抽象的数据结构,可以存储某些值,没有任何特定的顺序,也没有重复的值。。。

    除外

        2
  •  8
  •   Alex Siepman    11 年前

    我使用的简单方法是 Where(v => !secondList.Contains(v))

    当你这么做的时候,仍然有很多事情要做 secondList .

    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  
    

    我创建了一个扩展方法,完全没有distinct。用法示例:

    var result2Strings = firstStrings.ExceptAll(secondStrings).ToList(); // null, 3, 3
    

    它就是这样做的:

    enter image description here

    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的变体)

        3
  •  2
  •   Orace    4 年前

    鉴于 A = [1, 2, 2, 3, 3, 3] B = [3] .

    • A.Except(B); 退货 [1, 2] 就像格雷格·比奇在书中解释的那样 his response
    • A.ExceptAll(B); response ,返回 [1, 2, 2, 3, 3] (我发现这个名字模棱两可)。
    • A.Where(v => !B.Contains(v)) [1, 2, 2]

    我想OP变通是所期望的行为,而这个行为没有被处理。

    OP解决方案的主要问题是 List<T>.Contains(T) O(n) Where 也是 O(n²) 及时(对于同等尺寸的A和B)和 O(1)

    我们能做到 在内存中使用哈希集:

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