【问题标题】:equivalent LinkedHashmap in C++?C ++中的等效LinkedHashmap?
【发布时间】:2017-06-23 15:33:00
【问题描述】:

我有一个 Java 程序,我想将其转换为 C++。因此,Java 代码中使用了 Linkedhashmap 数据结构,我想将其转换为 C++。 C++ 中 LinkedHashmap 是否有等效的数据类型?

我尝试使用std::unordered_map,但是它不保持插入顺序。

【问题讨论】:

  • 不,你没有。这是一个丑陋的数据结构,难怪没有人建议对其进行标准化
  • @DavidHaim 嗯。那么,如何创建具有可预测迭代顺序的 hashmap?
  • 您首先需要问自己为什么哈希图中的插入顺序很重要..
  • 您需要“全局”插入顺序还是“本地”仅用于重复键?多值哈希映射。
  • 我通常使用 LinkedHashMap 作为 LRUCache,因为它的 removeEldest() 方法很容易支持大小和时间限制的缓存。其他寻找相同内容的人可能会关注这个问题而不是stackoverflow.com/questions/2504178/lru-cache-design

标签: c++ unordered-map linkedhashmap


【解决方案1】:

这就是我的做法:

    map<TKey, set<MyClass<K1,K2>, greater<MyClass<K1, K2>>>> _objects; // set ordered by timestamp. Does not guarantee uniqueness based on K1 and K2.
    map<TKey, map<K2, typename set<MyClass<K1, K2>, greater<MyClass<K1, K2>>>::iterator>> _objectsMap; // Used to locate object in _objects

添加对象id

    if (_objectsMap[userId].find(id) == _objectsMap[userId].end())
       _objectsMap[userId][id] = _objects[userId].emplace(userId, id).first;

删除对象id

   if (_objectsMap[userId].find(id) != _objectsMap[userId].end()) {
       _objects[userId].erase(_objectsMap[userId][id]);
       _objectsMap[userId].erase(id);
   }

要检索,说从特定对象 id 开始的列表中最近的 size 对象:

    vector<K2> result;
    if (_objectsMap[userId].find(id) != _objectsMap[userId].end() && _objectsMap[userId][id] != _objects[userId].begin()) {
        set<MyClass<K2, K2>, greater<MyClass<K1, K2>>>::iterator start = _objects[userId].begin(), end = _objectsMap[userId][id];
        size_t counts = distance(_objects[userId].begin(), _objectsMap[userId][id]);
        if (counts > size)
            advance(start, counts - size);        
        transform(start,
            end,
            back_inserter(result),
            [](const MyClass<K1, K2>& obj) { return obj.ID(); });
    }
    return result;

【讨论】:

    【解决方案2】:

    密钥迭代的插入顺序合约可以通过 log(n) 性能的平衡树来实现。这比在列表中维护键要好,因为删除项目需要 n 查找时间。我的口头禅是永远不要把你查到的东西放在一个列表中。如果不必对其进行排序,请使用哈希。如果应该排序,请使用平衡树。如果您要做的只是迭代,那么列表就可以了。 在 c++ 中,这将是 std::map,其中键是项目引用,值是插入顺序,键使用红黑树进行排序。见:Is there a sorted container in STL

    【讨论】:

    • 从 LinkedHashMap 中移除键是固定时间的,不是线性的。为了实现这一点,hashmap 条目必须维护对相应链表条目的引用。
    【解决方案3】:

    C++ 不提供具有模仿 Java 的 LinkedHashMap&lt;K,V&gt; 行为的集合模板,因此您需要将顺序与映射分开维护。

    这可以通过将数据保存在std::list&lt;std::pair&lt;K,V&gt;&gt; 中来实现,并保留一个单独的std::unordered_map&lt;k,std::list::iterator&lt;std::pair&lt;K,V&gt;&gt;&gt; 映射以便通过键快速查找项目:

    • 添加项时,将对应的键/值对添加到列表末尾,并将键映射到迭代器std::prev(list.end())
    • 按键删除项目时,查找其迭代器,将其从列表中删除,然后删除映射。
    • 替换项目时,首先从无序映射中查找列表迭代器,然后将其内容替换为新的键值对。
    • 在迭代值时,只需迭代 std::list&lt;std::pair&lt;K,V&gt;&gt;

    【讨论】:

    • 谢谢,我想这是我需要做的。它会增加更多的复杂性,但至少它仍然会保持顺序。再次感谢
    • 由于这是公认的答案,我仍然发现有必要指出这比 LinkedHashMap 更糟糕:1)按键查找时的附加间接 2)擦除所需的哈希查找-迭代器。条目包含两个链表(插入顺序和哈希桶)的指针的集成解决方案没有这些缺点。有关系吗?很难说/取决于。提议的解决方案严格是否不如这样的 LinkedHashMap?是的。
    • 如果我需要对std::list&lt;std::pair&lt;K,V&gt;&gt; 进行排序怎么办?
    • @KokHowTeh 您永远不需要明确地对列表进行排序。它的存在是为了维护迭代的顺序,并保持值。如果您需要在键上迭代映射,请使用std::map,它会自动完成。如果需要按排序顺序迭代一次,将数据复制到向量中并排序一次;它会更快。
    • 这是一个有效的用例。例如,当我将一批商品添加到列表中时需要进行排序,并根据排序条件维护顺序。
    猜你喜欢
    • 1970-01-01
    • 2011-04-05
    • 1970-01-01
    • 2013-10-30
    • 2012-07-31
    • 2011-11-05
    • 2020-12-31
    • 2020-12-09
    • 2010-10-20
    相关资源
    最近更新 更多