【问题标题】:Order of unordered_map changes on assignment分配时 unordered_map 更改的顺序
【发布时间】:2018-01-19 01:47:24
【问题描述】:

我很好奇这种行为。我发现分配一个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 上没有维护任何特定顺序,因为内部是一个哈希表,因此元素插入可以在任何地方结束,并且重新哈希将混合所有内容。

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

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

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

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

【问题讨论】:

  • 我喜欢你认识到 unordered 地图没有内部顺序的部分......然后仍然想知道为什么顺序不一致
  • @CoryKramer 不过,这是一个很好的问题。问题是为什么没有按原样复制后备存储;为什么要重新排列?
  • @CoryKramer 是的,我知道这是根据标准的答案。但是 OP 的问题是关于 为什么 它是以这种方式实现的。即使这是特定于实现的细节,这也是一个好问题(对于 SO 来说可能不是一个好问题,但这是一个好问题)
  • 你使用的是什么实现。 this 给出“预期”结果
  • @GonzaloSolera 如果你添加一个m4 添加assign m3 到它m4 看起来像m2

标签: c++ dictionary unordered-map


【解决方案1】:

由于显然这是特定于实现的(毕竟这是一个 无序 映射),我将进行有根据的推测。

如果markjohn 具有相同的散列并且对于有问题的桶的数量发生冲突,并且实现使用链接,我们也许能够解释这一点。如果链式实现在前面插入新项目(即使对于单链表也是恒定时间),那么每次分配容器时,链式项目的顺序都会被交换。

【讨论】:

  • 我觉得 markjohn 不太可能有相同的哈希值,如果是这样,问题应该通过使用不同的字符串来解决,这似乎不是案例(例如wandbox.org/permlink/hFVcM6fuLAG72rzx)。当然,不同的字符串也可能发生冲突,但不难找到不发生冲突的字符串。
【解决方案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++ 的unordered_map header

【讨论】:

猜你喜欢
  • 2020-12-20
  • 2016-03-14
  • 1970-01-01
  • 2015-09-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多