计算2个列表中存在的项目

Jul*_*ary 22 c# linq list count intersect

我有两个int型ListList AList B.我要检查多少项目List A是那里List B.我能够做到这一点,但是foreach因为优化是我的代码中的主要目标,所以我可以避免这种有效方式.

List<int> A = new List<int>;
List<int> B = new List<int>;
// Some logic....item added in both lists. Then

foreach(var item in A)
{
    if (B.Contains(item))
    {
        // Subtract number of duplicates
    }
}
Run Code Online (Sandbox Code Playgroud)

我尝试使用IntersectAny,但是返回bool所以我无法完全应用它们.

It'*_*ie. 25

B.Intersect(A).Count(); //should do the job
Run Code Online (Sandbox Code Playgroud)


Adr*_*tti 10

标准实现B.Intersect(A).Count()具有简短且易读的大优势,除非您有一个测量的性能问题,您应该使用它.

当性能是您可以介绍的问题时HashSet<int>,它是资源使用和搜索时间的良好折衷.但是因为你担心性能,我们应该进行一些测试(我使用的是我写的这个免费工具):

CPU:1.8 GHz Pentium Core 2 Duo
迭代次数:100
每个列表中的项目数:1000

A.Where(a => B.Contains(a)).Count():8338蜱
A.Intersect(B).Count():288蜱
B.Count - B.Except(A).Count():313蜱

现在让我们HashSet<int>在我们的测试中介绍(从任何其他答案中选择实现):

HashSet<int>:163滴答

它表现得更好.我们可以做得更好吗?如果输入范围是已知的(和限制),你可以做很多比这更好的使用BitArray.在这个例子中,我假设(为简单起见)只有正数,但它很容易适应.

public static int UseBitArray(int range, List<int> listA, List<int> listB) {
    var BitArray array = new BitArray(range);
    for (int i = 0; i < listA.Count; ++i)
        array[listA[i]] = true;

    int count = 0;
    for (int i = 0; i < listB.Count; ++i) {
        if (array[listB[i]])
            ++count;
    }

    return count;
}
Run Code Online (Sandbox Code Playgroud)

它的表现如何?

BitArray:95滴答

性能比较

它只需要第二好的方法(HashSet<int>)的58%.我甚至不和别人比较.请注意,它大量使用内存并且范围很广(比方说Int32.MaxValue / 2)它使用了大量内存(而且它的大小仅限于Int32.MaxValue那时你不能拥有完整的有符号32位整数范围.如果它的局限性不是问题那么你一定要配合它.

另请注意,如果您可以对输入做一些假设,那么您可以进一步优化搜索功能(例如,如果您可以假设已订购集合).

它们如何按比例放大(Y轴刻度为对数):

与不同输入集的性能比较

请注意,ExceptIntersect项目数量增长时表现更好.还要注意,对于这样的平凡对象(整数),并行执行它不会有任何性能提升(另请参阅查找两个字符串列表之间的差异):比较是如此微不足道,以至于开销和同步化高于收益(除非它是非常多的项目上的一个调整良好的算法).

如果你真的在寻找最后一点性能提升,你甚至可以实现自己的BitArray类(没有不需要的东西和错误检查):

sealed class FastBitArray {
    public FastBitArray(int length) {
        m_array = new int[((length - 1) / 32) + 1];
    }

    public bool this[int index] {
        get {
            return (m_array[index / 32] & (1 << (index % 32))) != 0;
        }
        set {
            if (value)
                m_array[index / 32] |= (1 << (index % 32));
            else
                m_array[index / 32] &= ~(1 << (index % 32));
        }
    }

    private int[] m_array;
}
Run Code Online (Sandbox Code Playgroud)

请注意,在setter中有一个分支,我们不必担心将其优化掉,因为true分支预测器的模式很容易(总是).没有性能提升使它比这更复杂.

最新测试:

迭代次数:100
每个列表中的项数:1000000

HashSet<int>:144748蜱
BitArray:37292蜱
FastBitArray:28966蜱

让我们直观地比较它们(蓝色系列测试1000项,橙色系列是1,000,000; Y轴是对数,以便与1k系列轻松比较).我们知道的方法很慢,只是省略了:

绩效对比图1

相同的数据仅显示1M系列和线性Y轴:

绩效比较图2


Gia*_*los 5

A.Where(a=>B.Contains(a)).Count ()
Run Code Online (Sandbox Code Playgroud)