Order of unordered_map changes on assignment

徘徊边缘 提交于 2019-12-10 16:00:58

问题


I'm curious about this behaviour. I found that assigning an unordered_map changes the internal order of the unordered map, without any 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;

outputs:

mark sarah john 
john sarah mark 
mark sarah john

I know that there isn't any specific order maintained on an unordered_map due to the fact that internally is a hash table, so an element insertion can end anywhere and a re-hash will mix it all.

However, here the order is changing just after an assignment. I expected the order to be the same, as I thought it would just copy the underlying storage.

The first explanation I thought was that maybe unordered_map is taking advantage of the copy to re-hash the new map into a more optimal arrangement. However, I tried to repeat an assignment on a new map (m3) from m2 and the order of m2 is not preserved in m3.

Why does assigning the map change the order?

My compiler is Apple LLVM version 8.1.0 (clang-802.0.42)


回答1:


This is an implementation detail of 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

If we assume you are using C++11 or greater, then this basically works by clearing the old hashtable, then inserting the elements of __u into this vector.

That means that when you do:

m2 = m1;

It's roughly equivalent to the following code:

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

This doesn't happen when you use libstdc++, as its implementation of operator= is just = default (see libstdc++'s unordered_map header)




回答2:


Since obviously this is implementation-specific (it is an unordered map after all) I'm going to make an educated speculation.

If mark and john have the same hash and collide for the number of buckets in question, and the implementation uses chaining we may be able to explain this. If the chaining implementation inserts new items at the front (constant time even for a single-linked list) then every time you assign the container the chained-item order will be swapped.



来源:https://stackoverflow.com/questions/45620007/order-of-unordered-map-changes-on-assignment

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!