我对'unordered_map'这个名字感到很困惑。该名称表明钥匙根本没有订购。但我一直认为它们是按哈希值排序的。或者这是错的(因为名称暗示它们没有被订购)?
或者说不同:这是
typedef map<K, V, HashComp<K> > HashMap;
与
template<typename T>
struct HashComp {
bool operator<(const T& v1, const T& v2) const {
return hash<T>()(v1) < hash<T>()(v2);
}
};
与
相同typedef unordered_map<K, V> HashMap;
? (好吧,不完全是,STL会在这里抱怨,因为可能有键k1,k2,而且k1&lt; k2和k2&lt; k1都没有。你需要使用multimap
并覆盖等号检查。)
或者换句话说:当我遍历它们时,我可以假设键列表是按其哈希值排序的吗?
答案 0 :(得分:21)
在回答您编辑过的问题时,这两个片段根本不相同。 std::map
将节点存储在树结构中,unordered_map
将它们存储在哈希表*中。
密钥不按其“哈希值”的顺序存储,因为它们并未存储在任何的任何顺序中。它们存储在“桶”中,其中每个桶对应于一系列散列值。基本上,实现如下:
function add_value(object key, object value) {
int hash = key.getHash();
int bucket_index = hash % NUM_BUCKETS;
if (buckets[bucket_index] == null) {
buckets[bucket_index] = new linked_list();
}
buckets[bucket_index].add(new key_value(key, value));
}
function get_value(object key) {
int hash = key.getHash();
int bucket_index = hash % NUM_BUCKETS;
if (buckets[bucket_index] == null) {
return null;
}
foreach(key_value kv in buckets[bucket_index]) {
if (kv.key == key) {
return kv.value;
}
}
}
显然,这是一个严格的简化,真正的实现将更加先进(例如,支持调整buckets
数组的大小,可能使用树结构而不是桶的链表,等等),但是应该知道如何以任何特定顺序取回值。有关详细信息,请参阅wikipedia。
*从技术上讲,std::map
和unordered_map
的内部实现是实现定义的,但是标准要求暗示那些内部实现的操作具有某些Big-O复杂性< / p>
答案 1 :(得分:6)
“无序”并不意味着实现中某处没有线性序列。这意味着“你不能假设这些元素的顺序”。
例如,人们通常会认为条目将以与它们放入的顺序相同的顺序出现在哈希映射中。但它们不会,因为条目是无序的。
对于“按其哈希值排序”:哈希值通常取自整个整数范围,但哈希映射中没有2 ** 32个槽。通过将散列值的模数设为模数,散列值的范围将减少到插槽的数量。此外,当您向哈希映射添加条目时,它可能会更改大小以适应新值。这可能导致重新放置所有先前的条目,从而改变它们的顺序。
在无序数据结构中,您不能假设条目的顺序。
答案 2 :(得分:2)
正如unordered_map所暗示的那样,C ++ 0x标准没有指定排序。 unordered_map的明显排序将取决于实际实现的方便之处。
答案 3 :(得分:1)
如果您想要一个类比,请查看您选择的RDBMS。
如果在执行查询时未指定ORDER BY子句,则结果将返回“无序” - 即,无论数据库的顺序如何。订单未指定,系统可以自由地“订购”它们,以便获得最佳性能。
答案 4 :(得分:1)
你是对的,unordered_map
实际上是哈希排序的。请注意,大多数当前实现(在TR1之前)将其称为hash_map
。
IBM C / C ++编译器documentation备注如果您有最佳散列函数,则在查找,插入和删除任意元素期间执行的操作数量不依赖于序列中的元素,这意味着订单不是那么无序......
现在,它是哈希排序意味着什么?由于散列应该是不可预测的,根据定义,您不能对映射中元素的顺序进行任何假设。这就是它在TR1中重命名的原因:旧名称提示订单。现在我们知道实际使用了一个订单,但你可以忽略它,因为它是不可预测的。