【问题标题】:Immutable Collection Hashcode不可变集合哈希码
【发布时间】:2016-10-06 19:06:51
【问题描述】:

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

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

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

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

【问题讨论】:

  • HashCode 不是好主意,因为可能会发生冲突。在现实生活中,哈希码用于表示对象“可能相等”或“绝对不相等”。
  • @nopeflow "绝对不相等" 足以避免对每个项目进行更昂贵的 SequenceEqual 检查...仅当哈希码相等时才能使用 SequenceEqual ....
  • @L.B Yeep,这是提高性能的正确和好地方 - 但我们不能只依赖哈希码 ;)
  • @Darren - 据我所知,这些集合不会返回任何集合的哈希码......而且我没有看到任何可以节省我时间的东西,而不是仅使用数组或 IEnumerable。

标签: c# hash collections


【解决方案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);
}

基本相等

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

【讨论】:

  • 谢谢...您的列表中有SafeEquals - 我假设它映射到代码部分中BasicEqual 的方法。
  • 是的,我在写完答案后修改了它......我会编辑:)
猜你喜欢
  • 2020-06-07
  • 2021-09-05
  • 2019-05-03
  • 1970-01-01
  • 1970-01-01
  • 2018-01-05
  • 2011-02-25
  • 1970-01-01
  • 2012-11-13
相关资源
最近更新 更多