unordered_map 的顺序更改分配
Order of unordered_map changes on assignment
我很好奇这种行为。我发现分配一个 unordered_map
改变了无序映射的内部顺序,没有任何 insertion/deletion:
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
上没有维护任何特定顺序,因为内部是一个哈希 table,因此元素插入可以在任何地方结束并重新哈希会混合所有。
但是,此处的顺序在分配后立即发生变化。我希望顺序相同,因为我认为它只会复制底层存储。
我想到的第一个解释可能是 unordered_map
正在利用副本将新地图重新散列为更优化的排列。但是,我尝试在 m2 的新地图 (m3) 上重复赋值,但 m3 中未保留 m2 的顺序。
为什么分配地图会改变顺序?
我的编译器是 Apple LLVM 版本 8.1.0 (clang-802.0.42)
因为显然这是特定于实现的(毕竟它是一个 无序 映射)我将进行有根据的推测。
如果 mark
和 john
具有相同的散列并在有问题的桶数上发生冲突,并且实现使用链接,我们也许可以解释这一点。如果链式实现在前面插入新项目(即使对于单链表也是恒定时间),那么每次您分配容器时,链式项目的顺序都会被交换。
这是 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++, as its implementation of operator=
is just = default
(see libstdc++'s unordered_map header)
时不会发生这种情况
我很好奇这种行为。我发现分配一个 unordered_map
改变了无序映射的内部顺序,没有任何 insertion/deletion:
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
上没有维护任何特定顺序,因为内部是一个哈希 table,因此元素插入可以在任何地方结束并重新哈希会混合所有。
但是,此处的顺序在分配后立即发生变化。我希望顺序相同,因为我认为它只会复制底层存储。
我想到的第一个解释可能是 unordered_map
正在利用副本将新地图重新散列为更优化的排列。但是,我尝试在 m2 的新地图 (m3) 上重复赋值,但 m3 中未保留 m2 的顺序。
为什么分配地图会改变顺序?
我的编译器是 Apple LLVM 版本 8.1.0 (clang-802.0.42)
因为显然这是特定于实现的(毕竟它是一个 无序 映射)我将进行有根据的推测。
如果 mark
和 john
具有相同的散列并在有问题的桶数上发生冲突,并且实现使用链接,我们也许可以解释这一点。如果链式实现在前面插入新项目(即使对于单链表也是恒定时间),那么每次您分配容器时,链式项目的顺序都会被交换。
这是 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++, as its implementation of operator=
is just = default
(see libstdc++'s unordered_map header)