不可变的收集哈希码

时间:2016-10-06 19:06:52

标签: c# hash collections

我的对象将有一个字节数组,它可能有数千个元素。该数组将在构造期间设置,然后永远不会更改。我需要能够比较来自2个独立对象的数组,看看它们是否完全相同。

我知道我可以使用Enumerable.SequenceEqual来比较两个值,但这有一个我想避免的开销。

我的计划是在生成集合并存储该哈希值后立即使用类似Good GetHashCode() override for List of Foo objects respecting the order的内容进行比较。

我想知道是否有一个不可变的集合类型构建到C#或.Net已经这样做了,或者如果有一个更好的选择我忽略了。

1 个答案:

答案 0 :(得分:1)

我已经汇总了一些比较字节数组的不同方法,我使用了10000的任意数组长度,并假设两个比较数组的长度相同(因为"宽相"长度检查显然不是很有趣:))

也许您可以使用它作为在比较数组是否相等时决定使用哪种方法的基础。

结果是三个场景的5次迭代的平均值(相等,第一个元素不同,最后一个元素不同),时间以毫秒为单位。

---------------
Identical elements
---------------
SequenceEqual: 5.98142
BasicEqual: 0.11864
UnsafeMemCmp: 0.15542
SafeMemCmp: 0.12896
---------------
First element different
---------------
SequenceEqual: 0.00056
BasicEqual: 0.00012
UnsafeMemCmp: 0.0002
SafeMemCmp: 0.00182
---------------
Last element different
---------------
SequenceEqual: 0.14942
BasicEqual: 0.03178
UnsafeMemCmp: 0.0015
SafeMemCmp: 0.00326
---------------

我选择的4种方法是:

SequentalEqual

static bool SequenceEqual(byte[] arr1, byte[] arr2)
{
    return arr1.SequenceEqual(arr2);
}

BasicEqual

static bool BasicEqual(byte[] arr1, byte[] arr2)
{
    for (var i = 0; i < 10000; i++)
        if (arr1[i] != arr2[i])
            return false;
     return true;
}

UnsafeMemCmp

[DllImport("msvcrt.dll", CallingConvention = CallingConvention.Cdecl)]
static extern unsafe int memcmp(byte* b1, byte* b2, int count);

static unsafe bool UnsafeMemCmp(byte[] arr1, byte[] arr2)
{
    fixed (byte* b1 = arr1, b2 = arr2)
    {
        return memcmp(b1, b2, 10000) == 0;
    }
}

SafeMemCmp

[DllImport("msvcrt.dll", CallingConvention = CallingConvention.Cdecl)]
static extern int memcmp(IntPtr b1, IntPtr b2, int count);

static bool SafeMemCmp(byte[] arr1, byte[] arr2)
{
    var a = Marshal.AllocHGlobal(arr1.Length);
    var b = Marshal.AllocHGlobal(arr2.Length);

    try
    {        
        Marshal.Copy(arr1, 0, a, arr1.Length);
        Marshal.Copy(arr2, 0, b, arr2.Length);

        return memcmp(a, b, 10000) == 0;
    }
    finally
    {
        Marshal.FreeHGlobal(a);
        Marshal.FreeHGlobal(b);
    }
}

完成后,使用以下方法运行测试:

static void RunTest(string name, Func<byte[], byte[], bool> action, byte[] a, byte[] b)
{
    TimeSpan total = TimeSpan.Zero;

    for (var i = 0; i < 5; i++)
    {
        _stopwatch.Reset();
        _stopwatch.Start();
        action(a, b);
        _stopwatch.Stop();
        total += _stopwatch.Elapsed;
    }

    Console.WriteLine(name + ": " + (total.TotalMilliseconds / 5));
}