在C#中获取两个数组之间的“差异”?

70

假设我有这两个数组:

var array1 = new[] {"A", "B", "C"};
var array2 = new[] {"A", "C", "D"};

我想要获取两者之间的差异。我知道我可以用几行代码来实现这个功能,但我想确保自己没有漏掉任何内置的语言特性或LINQ扩展方法。

理想情况下,我希望得到以下三个结果:

  • 不在array1中,但在array2中的项(“D”)
  • 不在array2中,但在array1中的项(“B”)
  • 在两者中都存在的项
5个回答

131

如果您可以使用LINQ,您可以使用ExceptDistinct。 您在问题中要求的集合分别为:

- array2.Except(array1)
- array1.Except(array2)
- array1.Intersect(array2)

你知道这个有什么性能保证吗?可能需要先对每个数组进行排序的副本。我在MSDN上找不到任何相关信息。 - Eclipse
2
不,它不会创建一个已排序的副本。它从被排除的序列中创建一个集合,然后迭代源序列,产生任何不在被排除序列中的元素。 - Jon Skeet
4
(当我说“set”时,指的是“哈希集合”。) - Jon Skeet
谢谢,我本来希望这个能在我的应用程序中使用,但由于我的数据已经预排序并且非常大,速度损失太高了。 - Eclipse
在尝试过Linq之前,我不会轻易放弃它。它和编译器有一系列的优化,可以让它变得出奇地快。在最新版本的框架中,AVX扩展被用来在某些场景下产生真正令人惊叹的性能。比起一个for循环,速度要快得多...先试试它,看看它是否真的像你想象的那么慢...我怀疑它不会。 - Drunken Code Monkey

13

来自MSDN 101 LINQ示例....

public void Linq52() {
    int[] numbersA = { 0, 2, 4, 5, 6, 8, 9 };
    int[] numbersB = { 1, 3, 5, 7, 8 };

    IEnumerable<int> aOnlyNumbers = numbersA.Except(numbersB);

    Console.WriteLine("Numbers in first array but not second array:");
    foreach (var n in aOnlyNumbers) {
        Console.WriteLine(n);
    }
}

4

我曾经需要处理非常大的数据集,类似于这样的事情。如果你只处理几千个数据,使用Linq会更清晰明了。但是,如果你知道你的数组已经预先排序好了,那么运行像这样的合并操作可以显著提高速度,因为它只通过一次数据并且不需要像Linq版本那样分配太多内存。

int iA = 0;
int iB = 0;
List<int> inA = new List<int>();
List<int> inB = new List<int>();
List<int> inBoth = new List<int>();
while (iA < numbersA.Length && iB < numbersB.Length)
{
    if (numbersA[iA] < numbersB[iB])
    {
        inA.Add(numbersA[iA++]);
    }
    else if (numbersA[iA] == numbersB[iB])
    {
        inBoth.Add(numbersA[iA++]);
        ++iB;
    }
    else
    {
        inB.Add(numbersB[iB++]);
    }
}
while (iA < numbersA.Length)
{
    inA.Add(numbersA[iA++]);
}
while (iB < numbersB.Length)
{
    inB.Add(numbersB[iB++]);
}

如果您要处理数十万个值,才真正需要这样做。


4
以下是LINQ扩展方法的基准测试结果。这些结果是在开发实际程序期间获得的。
测试内容: 两个列表(lst1和lst2),每个列表包含大约250000个对象。每个对象(类别Key)包含一个字符串和一个整数。第二个列表大多包含与第一个列表相同的条目,但添加了一些新条目并删除了一些条目。
我测试了Except扩展方法。
var except = lst2.Except(lst1);
List lst = except.ToList();
这两行代码生成了600个“新添加”的项目列表。我使用StopWatch对象计时。速度非常快:220毫秒。我使用的计算机不是“速度怪物”。核心2双T7700- 2.4 GHz。
备注:
以下是实现IEquatable接口的类Key。
public class Key : IEquatable<Key>
{
    public int Index { get; private set; }
    public string Name { get; private set; }

    public Key(string keyName, int sdIndex)
    {
        this.Name = keyName;
        this.Index = sdIndex;
    }

 // IEquatable implementation
    public bool Equals(Key other)
    {
        //Check whether the compared object is null.
        if (Object.ReferenceEquals(other, null)) return false;
        //Check whether the compared object references the same data.
        if (Object.ReferenceEquals(this, other)) return true;
        //Check whether the products' properties are equal.
        return Index.Equals(other.Index) && Name.Equals(other.Name);
    }

    // If Equals() returns true for a pair of objects 
    // then GetHashCode() must return the same value for these objects.
    public override int GetHashCode()
    {
        //Get hash code for the name field if it is not null.
        int hashKeyName = Name == null ? 0 : Name.GetHashCode();
        //Get hash code for the index field.
        int hashKeyIndex = Index.GetHashCode();
        //Calculate the hash code for the Key.
        return hashKeyName ^ hashKeyIndex;
    }
}

1
另一种解决方案也可以像下面这样:

int[] arr1 = new int[] { 45, 26, 99, 55, 36 };
int[] arr2 = new int[] { 45, 26, 99, 20, 36 };

var res = arr1.Union(arr2).Except(arr1.Intersect(arr2));

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