【问题标题】:Why does `std::unordered_map` "speak like the Yoda" - re-arrange elements?为什么`std::unordered_map`“像尤达一样说话” - 重新排列元素?
【发布时间】:2014-03-05 16:01:16
【问题描述】:

在以下示例中尝试编写 std::unordered_mapstd::string 键时,键的写入顺序与初始化列表给出的顺序不同:

#include <iostream>
#include <unordered_map>


class Data
{
    typedef std::unordered_map<std::string, double> MapType; 
    typedef MapType::const_iterator const_iterator;

    MapType map_; 

    public: 

        Data(const std::initializer_list<std::string>& i)
        {
            int counter = 0; 
            for (const auto& name : i)
            {
                map_[name] = counter; 
            }
        }


        const_iterator begin() const
        {
            return map_.begin(); 
        }

        const_iterator end() const
        {
            return map_.end(); 
        }

};

std::ostream& operator<<(std::ostream& os, const Data&  d)
{
    for (const auto& pair : d)
    {
        os << pair.first << " ";  
    }
    return os; 
}

using namespace std;

int main(int argc, const char *argv[])
{
    Data d = {"Why", "am", "I", "sorted"}; 

    // The unordered_map speaks like Yoda.
    cout << d << endl;

    return 0;
}

我希望看到“我为什么要排序”,但我得到了类似 Yoda 的输出:

sorted I am Why 

阅读unordered_maphere,我看到了这个:

在内部,元素不按任何特定顺序排序,而是组织成桶。元素放入哪个桶完全取决于其键的哈希值。这允许快速访问单个元素,因为一旦计算哈希,它指的是元素被放入的确切存储桶。

这就是为什么元素的排序方式与初始化列表中的不同吗?

当我希望以与初始值设定项列表相同的方式对键进行排序时,我应该使用什么数据结构?我应该在内部保留一个字符串向量以某种方式保存参数顺序吗?可以通过选择特定的散列函数以某种方式关闭存储桶组织吗?

【问题讨论】:

  • “当我希望以与初始化列表相同的方式对键进行排序时,我应该使用什么数据结构?” 名称中可能没有unordered . ;-)
  • 好吧,我已经在使用unordered_map,它的哈希函数仍然会重新排列键。
  • 试试普通的map。这行得通吗?
  • @graham.reeds,没有,因为在这种情况下,键是使用字符串比较进行排序的。
  • 听起来像是 Boost.Multiindex 的工作。可以根据上下文以多种方式访问​​多索引容器。例如,您可以将散列索引容器(用于基于键的查找)与有序索引容器(用于有序、顺序查找)混合。

标签: c++ c++11 unordered-map


【解决方案1】:

当我希望以与初始值设定项列表相同的方式对键进行排序时,我应该使用什么数据结构?我应该在内部保留一个字符串向量以某种方式保存参数顺序吗?

也许您实际上想要的只是(键、值)对的列表/向量?

如果您希望 O(1) 查找(哈希图)和迭代都以与插入相同的顺序 - 那么是的,使用 vectorunordered_map 听起来是个好主意。例如,Django 的 SortedDict (Python) 就是这样做的,这里是灵感来源:

https://github.com/django/django/blob/master/django/utils/datastructures.py#L122

Python 2.7 的 OrderedDict 有点花哨(映射值指向双向链表链接),请参阅:

http://code.activestate.com/recipes/576693-ordered-dictionary-for-py24/


我不知道标准库中现有的 C++ 实现,但这可能会让您有所收获。另见:

【讨论】:

    【解决方案2】:

    unordered_map 根据定义是无序的,因此您在按顺序访问地图时不应期望任何排序

    如果您不希望元素按键值排序,只需使用保持插入顺序的容器,可以是 vectordequelist 或其他 pair&lt;key, value&gt; 元素类型如果你坚持使用它。

    那么,如果在元素A之后添加一个元素B,它总是会在后面出现。这也适用于 initializer_list 初始化。

    您可能可以使用 Boost.MultiIndex 之类的东西来保持它按插入顺序和任意键排序。

    【讨论】:

    • 但是 map 不会按字母顺序对键进行排序吗?这绝对不是我想要的——顺序应该和初始化列表中的一样。
    • @BartekBanachewicz,但是在访问密钥时我有 线性复杂度 而不是 平均常数复杂度:每当我调用 operator["someStringName"] 我需要解析(在最坏的情况下)找到“someStringName”的所有键。
    • @tmaric 您可能应该在问题中更清楚地说明这一点。然后你很容易发现它是重复的。
    • Banachiewicz:老实说,我不明白你的意思——我明确要求保留初始化列表中元素的顺序。密钥分类容器立即不是一种选择。手工制作的容器使元素访问的复杂性变得更加糟糕。
    猜你喜欢
    • 2016-05-28
    • 2012-11-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-11-16
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多