【问题标题】:Can I use use Hashcode to directly lookup a value in a C# Dictionary我可以使用哈希码直接在 C# 字典中查找值吗
【发布时间】:2013-09-05 12:42:27
【问题描述】:

我有很多词典实例,这些词典的内容经常变化,大约有 100 个左右的条目(有时少得多)。 我需要经常查询这本词典。 我还不太确定,但我认为我的字典查找越来越昂贵,并且对我的表现产生了不利影响(这是一个关键问题)。

我可以缓存我的字符串键的哈希码

 int hc = MyStrKey.GetHasCode();

然后直接通过哈希码在字典中查找相应的值(如果可以的话)? 如果有可能,是否不推荐,是否值得加速?

通过频繁更改内容,我的意思是随着时间的推移,我正在从字典中添加和删除条目。

是否可以另一种做法是使用 int 键,我将实际字符串键的关联缓存到唯一(特定字典)int 键并改用字典?

我是否可能在这里吠错了树?

【问题讨论】:

  • Am I potentially barking up the wrong tree here? 这取决于您的查询的样子。请编辑您的帖子以显示查询。
  • 我不认为字典访问时间会以一种明显的方式逐渐变差,否则在相同的状态下。我敢打赌还有其他问题。
  • 如果您想经常查询该字典,请编写您自己的哈希表。我试了一下,速度提高了个位数。
  • @usr 我很想看看你是如何实现的
  • @Mishax 基本上你失去了很多抽象。您需要公开私有成员,更改调用者以使用不同的使用模式并使用二次幂表大小来消除慢模运算符。这些都不是透明的,它需要对字典和调用者进行极端的更改。

标签: c# dictionary


【解决方案1】:

我怀疑这会有很大的不同,但你可以做一些时间测试来找出答案。

您可以为 String 编写一个简单的不可变包装类来缓存哈希码,并将其用作键类型,例如:

public sealed class StringKey: IEquatable<StringKey>
{
    public StringKey(string key)
    {
        Contract.Requires(key != null);

        _key = key;
        _hashCode = key.GetHashCode();
    }

    public override int GetHashCode()
    {
        return _hashCode;
    }

    public bool Equals(StringKey other)
    {
        if (ReferenceEquals(null, other))
            return false;

        if (ReferenceEquals(this, other))
            return true;

        return (_hashCode == other._hashCode) && string.Equals(_key, other._key);
    }

    public override bool Equals(object obj)
    {
        if (ReferenceEquals(null, obj))
            return false;

        if (ReferenceEquals(this, obj))
            return true;

        return obj is StringKey && Equals((StringKey) obj);
    }

    public string Key
    {
        get
        {
            return _key;
        }
    }

    private readonly string _key;
    private readonly int    _hashCode;
}

但是,就像我说的,我怀疑这会产生很大的不同。

【讨论】:

  • 如果我缓存了哈希码,我将如何用它而不是实际的键来查询字典?
  • 你不会缓存哈希码,你会使用 StringKey 的实例作为字典的键而不是字符串。但是你必须把钥匙放在身边才能查到东西。如果您继续重新创建 StringKey 对象,那么每次创建对象时它们都会执行 GetHashCode() 。想一想,Dictionary 本身可能会缓存哈希码,因此它不需要继续为它包含的字符串键调用GetHashCode - 但它会为你用来查找的键调用GetHashCode值。
  • 是的,我刚刚查看了 Dictionary 的实现(使用 Reflector),当它存储键时,它只为每个键计算一次哈希码。当它查找键时,它只计算一次目标键的哈希码,然后用它来找到正确的值。所以我真的不认为你会从你可以做的任何类型的缓存中获得太多的加速(如果有的话)。
【解决方案2】:

请记住,哈希码并不是从字典(或哈希表)中查找项目所必需的唯一项。它只会让您更快地找到该项目所在的 bucket

两个不相等的项目具有相同的哈希码当然是可能的(并且并不少见)。字典使用哈希码查找存储桶,然后使用Equals 将该存储桶中的项目与给定键进行比较。

将其想象为按颜色将乐高积木组织在桶中 - 了解您需要的乐高积木的颜色有助于您更快找到它,但您仍然需要知道确切的颜色一块你需要找到正确的块。

那么你能在字典中通过哈希码查找项目吗?可能,但您仍然需要原始值来确定您获得了 正确 项。

【讨论】:

  • 对不起,我的意思是如果缓存hashcode,是否可以用来加快查找速度。但从到目前为止的帖子来看,它可能不会有太大帮助。似乎使用 int 作为键与字符串也不会有太大区别?
  • @Ryan 如果您的 Dictionary 实现正在使用 HashCodes 进行查找,那么不,没关系,如果它是 Int32 或 String,库将计算 HashCode - 检查它尝试计算表达式9.GetHashCode()
【解决方案3】:

我还不太确定,但我认为我的字典查找成本越来越高

首先进行测量并准确找出您要解决的问题。在具有 100 个键/值对的字典中进行查找应该非常快。

至于使用整数或字符串作为键,请注意这些项目的哈希码计算未在 FCL API 中定义,并且是特定于实现的。不可能做出笼统的陈述。

【讨论】:

  • 还没有进行精确的测量,只是比较一下我减少查询量的时候,以及只是做一个索引表查找的时候。查找表当然要快得多(但我不能在实践中使用它)。并且在减少查询数量时,我确实在包含组件的使用方面得到了相当大的提升。
  • 忘了说我用的是Unity4.2打包的mono,mono开发版本2.8.2。
  • 当然需要测量,只是为了比较我必须实施替代方案,这会很耗时。但可能没有其他方法知道。
猜你喜欢
  • 2022-01-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-11-02
  • 2019-10-18
  • 1970-01-01
  • 2015-05-29
  • 1970-01-01
相关资源
最近更新 更多