unordered_map的顺序在赋值时更改

时间:2017-08-10 17:26:21

标签: c++ dictionary unordered-map

我对这种行为感到好奇。我发现分配unordered_map会更改无序地图的内部顺序,而不会插入/删除任何内容:

unordered_map<int, string> m1;
unordered_map<int, string> m2;
unordered_map<int, string> m3;

m1[2] = "john";
m1[4] = "sarah";
m1[1] = "mark";

m2 = m1;
m3 = m2;

for(auto it = m1.begin(); it != m1.end(); ++it) {
    cout << it->second << " ";
}
cout << endl;
for(auto it = m2.begin(); it != m2.end(); ++it) {
    cout << it->second << " ";
}
cout << endl;
for(auto it = m3.begin(); it != m3.end(); ++it) {
    cout << it->second << " ";
}
cout << endl;

输出:

mark sarah john 
john sarah mark 
mark sarah john

我知道unordered_map上没有任何特定的顺序,因为内部是一个哈希表,因此元素插入可以在任何地方结束,重新哈希将混合所有。< / p>

但是,这里的订单在分配后才会发生变化。我希望订单是相同的,因为我认为它只会复制底层存储。

我认为的第一个解释是,unordered_map可能正在利用副本将新地图重新散列为更优化的排列。但是,我试图在m2的新地图(m3)上重复分配,m3的顺序不会保留。

为什么分配地图会改变顺序?

我的编译器是Apple LLVM版本8.1.0(clang-802.0.42)

2 个答案:

答案 0 :(得分:2)

因为很明显这是特定于实现的(毕竟它是无序的地图)我会做出有根据的推测。

如果markjohn具有相同的散列并且碰撞有问题的存储区数量,并且实现使用链接,我们可以解释这一点。如果链接实现在前面插入新项目(即使对于单链接列表也是常量时间),那么每次分配容器时,都会交换链式项目顺序。

答案 1 :(得分:2)

这是libc++的实施细节:

    _LIBCPP_INLINE_VISIBILITY
    unordered_map& operator=(const unordered_map& __u)
    {
#ifndef _LIBCPP_CXX03_LANG
        __table_ = __u.__table_;
#else
        if (this != &__u) {
            __table_.clear();
            __table_.hash_function() = __u.__table_.hash_function();
            __table_.key_eq() = __u.__table_.key_eq();
            __table_.max_load_factor() = __u.__table_.max_load_factor();
            __table_.__copy_assign_alloc(__u.__table_);
            insert(__u.begin(), __u.end());
        }
#endif
        return *this;
    }

From libc++'s unordered_map header

如果我们假设您使用的是C ++ 11或更高版本,那么这基本上可以通过清除旧的哈希表,然后将__u的元素插入到此向量中来实现。

这意味着当你这样做时:

m2 = m1;

它大致相当于以下代码:

m2.clear();
m2.max_load_factor(m1.max_load_factor());
m2.insert(m1.begin(), m1.end());

当您使用libstdc++时,这不会发生,因为operator=的实现只是= default(请参阅libstdc ++&#39; s unordered_map header