为什么`std :: unordered_map`“像尤达一样说话” - 重新安排元素?

时间:2014-03-05 16:01:17

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

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

std::unordered_map

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

#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;
}

sorted I am Why here上阅读,我看到了这一点:

  

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

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

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

2 个答案:

答案 0 :(得分:4)

  

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

也许您想要的只是(键,值)对的列表/向量?

如果你想要O(1)查找(hashmap)和迭代的顺序与插入顺序相同 - 那么是的,使用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 ++实现,但这可能会让你在某处。另见:

答案 1 :(得分:2)

根据定义,

unordered_map是无序的,因此在顺序访问地图时不应指望任何排序

如果您不希望元素按键值排序,只需使用一个保持插入顺序的容器,无论是vectordequelist还是其他,如果您坚持使用它,则为pair<key, value>元素类型。

然后,如果在元素A之后添加了一个快速B,它将始终出现。这适用于initializer_list初始化。

您可以使用类似Boost.MultiIndex的内容来保持按插入顺序和任意键排序。