【问题标题】:Hashmap equivalent in C++C++ 中的 Hashmap 等价物
【发布时间】:2017-07-03 05:57:19
【问题描述】:

我有一个应用程序(在 C++ 中),我需要在其中有一组字符串和整数之间的配对,即:

("david", 0)
("james", 1)
("helen", 2)
... 

如果我们使用 java (key, value) 定义,我需要能够 (1) 搜索以查看映射中是否存在键并 (2) 检索与给定字符串 (key) 关联的值。在 java 中工作时,我发现 HashMap 类型可以处理我需要的一切。

我也想用 C++ 做同样的事情。我做了一些谷歌搜索,发现在 C++ 2011 库中有一个 unordered_map 类型可以复制它。 我很好奇这是否是最好的方法。

在我的应用程序中,我对集合有以下规则

  1. 整数始终是连续的(根据示例)并从 0 开始。
  2. 整数值永远不会改变。
  3. 地图是在应用程序启动时创建的,不会更改,即它是不可变的。
  4. 字符串键没有重复项。
  5. 创建地图后,我不知道需要使用多少键(以及扩展整数值)。我的应用程序的参数之一是包含要使用的单词列表的文本文件的目录。
  6. 我不关心与此相关的启动时间成本。我需要主要任务(即 containsKey(..) 和 get(key) 尽可能快)。它会被称为很多。该应用程序以处理大型文本语料库(即维基百科)和形成单词/文档之间的共现矩阵为中心。

我认为不是同时存储整数和字符串,而是将字符串存储在某种列表类型中,然后返回索引,即 数据 = {“大卫”、“詹姆斯”、“海伦”、... }

然后类似 find_Map(data, key) 的东西返回它所在的索引(值)。我认为这可以通过首先按升序排序并应用搜索算法来加快速度。但同样,这只是一个猜测。

我很欣赏这是一个常见问题,并且存在许多不同的方法。我将编写一些不同的想法,但我认为最好先让小组看看你们的想法。

【问题讨论】:

  • 您应该改写问题,因为您将键与值混淆 - 例如,当您说“检索与给定字符串关联的键”时 - 在这里我认为字符串是键。
  • 干杯@davidback - 会的。
  • 你可以寻找结构。
  • 顺便说一句 - 假设你已经测量了你的程序并且确定这两个操作 contains() 和 find() 是花费时间的地方,这就是为什么你需要它们尽可能快 - 它也有必要知道有多少查找成功与未命中。换句话说,在搜索时找到密钥的可能性更大,还是没有找到?不过总的来说,unordered_map 没有任何问题,除非您真的时间有限。
  • 我建议删除 Java 标记广告,这与 Java 关系不大。并且还要与你的变量名保持一致:在 C++ 中没有称为 Integer 的东西。

标签: java c++ hashmap unordered-map


【解决方案1】:

你可以使用unordered_map<string,int>

【讨论】:

    【解决方案2】:

    根据您要存储的数据量,有两种可能:

    • 对于半大量的数据,我认为std::unordered_map<string, int> 就可以了
    • 如果您想处理大量数据,考虑更多专用的字符串存储数据结构可能会有所帮助,例如尝试,其中具有公共前缀的字符串存储在公共子树中。这也可以提高您的空间使用率,因为数据被某种压缩了。我所知道的最有效的实现是 marisa-trie 也用于 python pytries 包。

    【讨论】:

    • unordered_list 到底是什么?
    【解决方案3】:

    简单的答案当然是std::unordered_map。但是,为了获得更多功能和自动索引一致性,我们可以使用boost::multi_index_container

    例如:

    namespace bmi = boost::multi_index;
    
    // Define a custom container type
    using my_map = boost::multi_index_container<
        // It holds StringValue objects
        StringValue,
        bmi::indexed_by<
            // first index is called by_string, is a unique hashed index with constant time lookuo
            bmi::hashed_unique<bmi::tag<by_string>, bmi::member<StringValue, std::string, &StringValue::str>>,
    
            // second index is called by_value, is a unique hashed index with constant time lookup
            bmi::hashed_unique<bmi::tag<by_value>, bmi::member<StringValue, int, &StringValue::value>>,
    
            // second index is called ordered_by_value, is a unique ordered index with logarithmic time lookup
            bmi::ordered_unique<bmi::tag<ordered_by_value>, bmi::member<StringValue, int, &StringValue::value>>
        >
    >;
    

    在本例中,my_map 被定义为一个容器:

    • 持有StringValue对象

    • 通过对象的str 成员维护一个散列唯一索引

    • 通过对象的value 成员维护一个散列唯一索引

    • 通过对象的value 成员维护有序的唯一索引,以防我们希望按值枚举(例如)

    完整示例:

    #include <boost/multi_index_container.hpp>
    #include <boost/multi_index/indexed_by.hpp>
    #include <boost/multi_index/member.hpp>
    #include <boost/multi_index/hashed_index.hpp>
    #include <boost/multi_index/ordered_index.hpp>
    
    #include <boost/format.hpp>
    #include <string>
    #include <iostream>
    #include <iomanip>
    #include <cassert>
    #include <type_traits>
    
    // define a value object
    struct StringValue
    {
        std::string str;
        int         value;
    };
    
    // provide a way to stream the pair to an ostream
    std::ostream& operator <<(std::ostream& os, StringValue const& sv)
    {
        static const char fmt[] = R"__({ "str": %1%, "value": %2% })__";
        return os << boost::format(fmt) % std::quoted(sv.str) % sv.value;
    }
    
    struct by_string
    {
    };
    struct by_value
    {
    };
    struct ordered_by_value
    {
    };
    
    namespace bmi = boost::multi_index;
    
    // Define a custom container type
    using my_map = boost::multi_index_container<
        // It holds StringValue objects
        StringValue,
        bmi::indexed_by<
            // first index is called by_string, is a unique hashed index with constant time lookuo
            bmi::hashed_unique<bmi::tag<by_string>, bmi::member<StringValue, std::string, &StringValue::str>>,
            // second index is called by_value, is a unique hashed index with constant time lookup
            bmi::hashed_unique<bmi::tag<by_value>, bmi::member<StringValue, int, &StringValue::value>>,
            // second index is called ordered_by_value, is a unique ordered index with logarithmic time lookup
            bmi::ordered_unique<bmi::tag<ordered_by_value>, bmi::member<StringValue, int, &StringValue::value>>
        >
    >;
    
    template<class Array>
    struct ArrayEmitter
    {
        const Array& array;
    
        friend std::ostream& operator<<(std::ostream& os, ArrayEmitter const& em) {
            const char* sep = " ";
            os << "[";
            for (auto&& item : em.array) {
                os << sep << item;
                sep = ", ";
            }
            return os << " ]";
        }
    };
    
    template<class Array>
    auto emit_as_array(Array&& arr)
    {
        return ArrayEmitter<std::remove_cv_t<Array>> { arr };
    }
    
    int main()
    {
        my_map mm { { "B", 3 }, { "D", 1 }, { "A", 4 }, { "C", 2 } };
    
        // assert that we can't violate the indecies
        auto ib = mm.insert(StringValue{"E", 1});
        assert(ib.second == false);
    
        // iterate by string
        std::cout << "print by value index:\n";
        std::cout << emit_as_array(mm.get<by_string>()) << std::endl;
    
        std::cout << "\nprint by value index unordered:\n";
        std::cout << emit_as_array(mm.get<by_value>()) << std::endl;
    
        std::cout << "\nprint by value index ordered:\n";
        std::cout << emit_as_array(mm.get<ordered_by_value>()) << std::endl;
    
        std::cout << "\nfind an element by value in constant time:\n";
        auto&& name = mm.get<by_value>().find(2)->str;
        std::cout << name << std::endl;
    }
    

    预期输出:

    print by value index:
    [ { "str": "B", "value": 3 }, { "str": "D", "value": 1 }, { "str": "A", "value": 4 }, { "str": "C", "value": 2 } ]
    
    print by value index unordered:
    [ { "str": "B", "value": 3 }, { "str": "D", "value": 1 }, { "str": "A", "value": 4 }, { "str": "C", "value": 2 } ]
    
    print by value index ordered:
    [ { "str": "D", "value": 1 }, { "str": "C", "value": 2 }, { "str": "B", "value": 3 }, { "str": "A", "value": 4 } ]
    
    find an element by value in constant time:
    C
    

    文档:

    http://www.boost.org/doc/libs/1_62_0/libs/multi_index/doc/tutorial/index.html

    【讨论】:

    • 非常感谢您的帖子!我对 C++ 还是很陌生,所以我将不得不阅读这几次(一些我还不熟悉的语法。但再次感谢大家 - 我希望我能尽快“阅读”并理解这一点。
    猜你喜欢
    • 2010-11-19
    • 1970-01-01
    • 1970-01-01
    • 2013-11-04
    • 1970-01-01
    • 2011-04-02
    • 2021-10-15
    • 2013-10-20
    • 2011-04-10
    相关资源
    最近更新 更多