【问题标题】:Create hash map with parameters ordered in the way inserted使用以插入方式排序的参数创建哈希映射
【发布时间】:2018-01-15 09:21:55
【问题描述】:

我想更改 std::map (它是哈希函数),以便在迭代时以插入方式返回对。我尝试过使用无序地图,但没有成功。所以我想我必须创建一个哈希函数,它每次都会递增并返回更大的值。我怎样才能做到这一点?我不处理大数据,所以性能不是问题。

【问题讨论】:

  • 这听起来会破坏哈希映射的目的。为什么不使用对的列表或向量呢?
  • 每次递增并返回更大值的哈希函数这不是哈希函数。您将如何在地图中找到任何内容?
  • @manni66 当然是通过索引或迭代,与std::vector相同。
  • @Caninonos 它没有,您需要 O(1) 查找,而是基于插入顺序的迭代。这是 Python 中的常见习语,但您可以使用链表和 hashmap 来做到这一点,尽管有更有效的实现。
  • 如果“性能不是问题”,听起来向量是迄今为止最简单的答案

标签: c++ hashmap hash-function


【解决方案1】:

听起来你想要一个有序映射,一个允许 O(1) 查找但记住插入顺序(用于迭代顺序)的哈希映射。幸运的是,C++ 中存在一个有效的实现: https://github.com/Tessil/ordered-map

自述文件中的一个示例是:

#include <iostream>
#include <tsl/ordered_map.h>

int main() 
{
    tsl::ordered_map<char, int> map = {{'d', 1}, {'a', 2}, {'g', 3}};
    map.insert({'b', 4});
    map['h'] = 5;
    map['e'] = 6;

    map.erase('a');


    // {d, 1} {g, 3} {b, 4} {h, 5} {e, 6}
    for(const auto& key_value : map) {
        std::cout << "{" << key_value.first << ", " << key_value.second << "}" << std::endl;
    }

    return 0;
}

您也可以像 Python 的有序映射一样实现它,它使用链表来跟踪插入顺序。一个优点是更有效的删除(Tessil 的有序映射有O(n) 删除,而链表/映射将是平均O(1))。假设映射通常存储Key, T 类型的键/值类型,您将在链表中存储T,并将迭代器存储为内部 unordered_map 的值类型。然后,您将包装该类和任何迭代器,以确保它像普通的哈希图一样从外部运行。

基本大纲如下所示(如果不接受 O(n) 删除,则其余部分由您实现)。使用列表是因为它们不会在删除元素时使迭代器无效,并且映射用于查找正确的列表迭代器以按键删除。满足所有大的 O 问题,尽管有一些开销。

#include <list>
#include <unordered_map>

template <
    typename Key,
    typename T,
    typename Hash = std::hash<Key>,
    typename KeyEqual = std::equal_to<Key>,
    typename Allocator = allocator<std::pair<Key, T>>
>
class ordered_map
{
public:
    using key_type = Key;
    using mapped_type = T;
    // ....

private:
    using list_type = list<T, typename std::allocator_traits<Allocator>::template rebind_alloc<T>>;
    using list_iterator = typename list_type::iterator;
    using map_type = std::unordered_map<
        Key, list_iterator, Hasah, KeyEqual, 
        typename std::allocator_traits<Allocator>::template rebind_alloc<std::pair<Key, list_iterator>>
    >;

    list_type list_;
    map_type map_;
};

【讨论】:

    【解决方案2】:

    我认为最好使用:

    std::vector&lt;std::pair&lt;key,value&gt; myValues

    对于插入,您将使用这样的代码

    myValues.emplace_back(std::make_pair&lt;key,value&gt;(someKey, someValue));

    最好循环执行此操作。然后,当您想要按插入顺序获取项目时,您只需在索引上循环例如使用基于 for-ranged 的​​循环来按插入顺序输出元素:

    for(auto & pair : myValues) { std::cout << "Key: " << pair.first << " Value: " << pair.second << std::endl; }

    在 unordered_map 或常规地图容器上执行此操作对于此类容器来说是不好的用例。

    【讨论】:

    • 这不允许有效地查找地图中的项目。记住插入顺序的有序 hashmap 并不新鲜:它们实际上在 Python 中非常常见(它们目前是 Python 中的默认 hashmap 实现)。考虑到对于有序哈希图(记住插入顺序)发现的最流行的编程语言之一的巨大实用性,我强烈反对这对于这样的容器来说是一个糟糕的用例。
    • 我只是说对于来自 c++ STL 库的无序映射或常规映射来说,这将是一个糟糕的用例 :)
    • 可能没有使用自定义哈希函数。但是你实际上可以包装一个标准的 hashmap 和一个链表来非常有效地创建一个有序的 map。这实际上正是 CPython 所做的。我永远不会投反对票,但是将标准哈希图扩展到记住插入顺序的哈希图相当容易。
    • 是的,我同意。问题是,这取决于一个人想要做什么。如果您唯一想要的是按插入顺序进行迭代(我虽然是这种情况),最好使用 std::vector。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-10-01
    • 1970-01-01
    • 2023-04-08
    • 2021-06-11
    • 1970-01-01
    相关资源
    最近更新 更多