为什么"std::unordered_map""speak like the Yoda" - 重新排列元素?

Why does `std::unordered_map` "speak like the Yoda" - re-arrange elements?

本文关键字:新排列 元素 排列 the unordered std map speak 为什么 like Yoda      更新时间:2023-10-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_map,我看到了这个:

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

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

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

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

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

如果您希望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++实现,但这可能会让你有所收获。另请参阅:

  • 一个保留插入顺序的C++哈希映射
  • 一个跟踪插入顺序的std::映射

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

如果你不想让元素按键值排序,只要使用一个容器来保持你的插入顺序,无论是vectordequelist还是其他什么,如果你坚持使用pair<key, value>元素类型。

然后,如果在元素A之后添加一个元素B,它总是会在后面出现。initializer_list初始化也是如此。

你可能会使用类似Boost的东西。MultiIndex使其同时按插入顺序和任意键进行排序。