【问题标题】:key vs. hash in a std::unordered_mapstd::unordered_map 中的键与散列
【发布时间】:2019-01-13 16:14:11
【问题描述】:

我经常需要一个容器,其中哈希与任意对象相关联(如果两个不同的对象具有相同的哈希,理论上可能发生冲突)。

在 C++98 中,我将使用 template<class Key, class T> class std::map 使用 Key 作为在 T 上计算的哈希:

struct object;
typedef std::string object_hash;

object_hash compute_hash(const object& obj);

std::map<object_hash, object> hash_map;

object_hash insert_or_assign(const object& obj)
{
    object_hash hash = compute_hash(obj);
    hash_map[hash] = obj;
    return hash;
}

std::pair<bool, object> get_at(const object_hash& hash)
{
    std::map<object_hash, object>::iterator iter = hash_map.find(hash);
    if( iter == hash_map.end() )
        return std::pair<bool, object>(false, object());
    else
        return std::pair<bool, object>(true, iter->second);
}

但是从 C++11 开始,我们已经对容器进行了哈希处理,所以我预计会是这样的:

template<class T, class Key = std::hash<T>> class std::hashed_map

要求为T 类型提供自定义std::hash,但我们有

template<class Key, class T, class Hash = std::hash<Key>> class unordered_map

这不适用于我的关键是哈希本身的场景,并且没有与任意对象相关的其他“关键”概念。

和我预期的差不多:

template<class Key, class Hash = std::hash<Key>> class unordered_set

但没有基于哈希的查找函数。

在现代 C++ 中是否有一个使用哈希的内置容器并具有基于这些哈希的查找接口?

【问题讨论】:

  • 我认为您需要查看 wikipedia 以了解哈希映射是什么。你在 C++03 中所做的在很多层面上都是错误的。
  • 任何散列容器都需要一个真正的密钥来消除散列冲突,因为散列(通常)是有损的。因此,您既需要真实密钥又需要哈希的情况正是哈希用于可能产生冲突的快速查找的正常用例。为什么你想自己生成哈希而不是让容器调用对你来说可能是相同的哈希?
  • 所以也许我误用了“散列映射”这个术语,但这里的需求非常明确:将任意对象与散列相关联。该对象没有额外的“密钥”。这有点像 git,每个提交都有自己的哈希值,是的,理论上冲突是可能的。
  • 好吧,忽略碰撞的可能性在某些情况下可能是愚蠢的,但在其他情况下是可以接受的。然而,C++ 容器并不能那样工作,即使在哈希冲突的情况下,它们也能提供 100% 的可靠性。为什么要妥协?
  • @UlrichEckhardt 关键是在这种情况下,除了哈希之外,我没有任何其他密钥。 Git 使用 SHA-1 并忽略碰撞,因为它们在统计上不太可能发生(除非是故意的碰撞攻击)。

标签: c++ hashmap containers


【解决方案1】:

最初unordered_map 被称为hash_map,后来 ISO C++ 委员会明智地重命名它,因为std::mapstd::unordered_map 之间的重要区别是不是第一个使用二叉树而后者使用散列,第一个是有序的,而后者保证恒定时间复杂度。

所以std::unordered_map 在内部使用哈希的事实只不过是一个实现细节:如果 是自定义类型(键是不常见的自定义类型)。除此之外,你应该忘记这个容器的内部哈希。

尽管有一些 cmets,但如果您的密钥是哈希,那么您的 C++98 实现绝对没有问题。您可以在 C++ >= 11 中继续使用它,并在可能的情况下对其进行更新和更新。

【讨论】:

【解决方案2】:

你有一个映射而不是哈希映射;您的密钥是哈希的事实与容器无关。

关于唯一显着的特点是你很少关心哈希的顺序;所以无序地图可能是最好的。

采用您的旧解决方案,将 map 替换为无序映射,将 less 操作替换为 equal,并将哈希(可能向下)替换为 64 位。例如,经典的指针哈希就是reinterpret_cast&lt;unit_ptr&gt;( key )

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-06-15
    • 1970-01-01
    • 2014-09-27
    • 2011-12-28
    • 1970-01-01
    相关资源
    最近更新 更多