通过std :: map进行迭代的顺序是否已知(并由标准保证)?


158

我的意思是-我们知道std::map的元素是根据键排序的。因此,假设键是整数。如果从迭代std::map::begin()std::map::end()使用for,标准是否保证我将因此依次迭代具有键的元素(按升序排序)?


例:

std::map<int, int> map_;
map_[1] = 2;
map_[2] = 3;
map_[3] = 4;
for( std::map<int, int>::iterator iter = map_.begin();
     iter != map_.end();
     ++iter )
{
    std::cout << iter->second;
}

是否保证可以打印234或实现定义?


现实生活中的原因:我有一个std::mapint钥匙的。在极少数情况下,我想遍历所有具有关键意义而不是具体int价值的要素。是的,听起来std::vector是更好的选择,但是请注意我的“非常罕见的情况”。


编辑:我知道,的元素std::map已排序..无需指出(对于此处的大多数答案)。我什至在我的问题中写了它。
我在遍历容器时询问迭代器和顺序。感谢@Kerrek SB的回答。


6
如果您不知道:在现实生活中,您可以使用它map::upper_bound来寻找开始迭代的点。
史蒂夫·杰索普

我知道这一点,也知道我要开始迭代的确切位置。我只是在保证订单的情况下徘徊。
Kiril Kirov

如果您的键(数字索引)在整体上变化很大,那么稀疏向量将不明智。我正在使用类似的解决方案,其数字索引表示3维空间中的笛卡尔y坐标。在这种情况下使用向量将使我的内存占用量增加千兆字节。因此,我认为该载体不是万能药,离它很远。
乔纳森·诺伊菲尔德

我不明白这个问题,我将通过一次思想实验来解释为什么。如果您已经知道元素是有序的,那么怎么可能不进行迭代呢?如果排序不适用于迭代,那甚至意味着什么?在订单重要,可检测到的情况下还有哪些其他情况?(康斯坦丁(Konstantin)给出了答案。)
underscore_d

Answers:


176

是的,那是保证。此外,由比较运算符确定,*begin()为您提供最小和*rbegin()最大的元素,并且两个键值a以及b表达式!compare(a,b) && !compare(b,a)为真的两个键值被视为相等。默认比较功能为std::less<K>

排序不是幸运的奖励功能,它是数据结构的基本方面,因为排序用于确定两个键何时相同(根据上述规则)并执行有效的查找(本质上是二进制)搜索,其元素数量具有对数复杂性)。


std :: map是使用二进制树实现的,因此从技术上讲,不会执行二进制搜索。
jupp0r

11
@ jupp0r:将范围构造为二进制搜索树是在范围内实现二进制搜索的一种特殊方法。“二进制搜索”是一个抽象概念,而不是特定的实现。无论是通过偏移量跳入数组还是跟随链接节点都没有关系。这些只是“平分范围”的特定方式。
Kerrek SB 2015年

1
我知道这是一篇旧文章,但需要明确的是“有效查找”是相对的。从技术上讲,std::unordered_map具有更高效的O(1)查找时间。的优势std::map在于键排序,而不是查找。
亚当·约翰斯顿

42

这由C ++标准中的关联容器要求来保证。例如,参见C ++ 11中的23.2.4 / 10:

关联容器的迭代器的基本属性是它们
以键的降序遍历容器,其中
非降序是通过用于构造它们的比较来定义的。
对于任何两个可解除引用的迭代器i和j,使得从i到j的距离为
正,
  value_comp(* j,* i)==否

和23.2.4 / 11

对于具有唯一键的关联容器,条件越强,
  value_comp(* i,* j)!=否

32

我认为数据结构混乱。

在大多数语言中,a map只是一个AssociativeContainer:它将键映射到值。在“较新的”语言中,通常使用哈希映射来实现,因此不保证顺序。

但是,在C ++中,情况并非如此:

  • std::map是一个排序的关联容器
  • std::unordered_map 是在C ++ 11中引入的基于哈希表的关联容器

因此,为了阐明订购保证。

在C ++ 03中:

  • std::setstd::multisetstd::mapstd::multimap,保证按照键进行排序(以及标准提供的)
  • std::multiset和中std::multimap,标准没有对等效元素(即比较相等的元素)施加任何顺序保证

在C ++ 11中:

  • std::setstd::multisetstd::mapstd::multimap,保证按照键进行排序(以及标准提供的)
  • std::multiset和中std::multimap,标准强加了等效元素(比较相等的元素)根据其插入顺序(先插入先插入)进行排序。
  • std::unordered_*顾名思义,容器是无序的。最值得注意的是,修改容器时(插入/删除时),元素的顺序可能会更改。

当标准说元素以某种方式排序时,表示:

  • 迭代时,您会看到定义的顺序中的元素
  • 当反向迭代时,您会看到相反顺序的元素

我希望这可以消除任何混乱。


抱歉,这与我的问题没有任何关系:)我知道订购的是哪个,而不是。当我遍历元素时,我正在请求顺序。
Kiril Kirov

10
@KirilKirov:嗯,一个有序的关联容器的定义是,当迭代它时,元素是有序的。
Matthieu M.

好吧,我想你是对的,但我不知道,那正是我要问的:)
Kiril Kirov

4

是否保证可以打印234或已定义实现?

是的,std::map是一个已排序的容器,由Key提供的排序Comparator。因此可以保证。

我想用键遍历所有元素,而不是具体的int值。

这肯定是可能的。


3

是的...中的元素std::map具有严格的弱序,这意味着元素将由集合组成(即,不会有重复的“相等”键),并且相等性是通过对任何两个键A和B,即如果键A不小于键B,而键B不小于A,则键A等于键B。

话虽这么说,std::map如果该类型的弱排序是模棱两可的,则无法正确排序a的元素(在您的情况下,您使用整数作为键类型,这不是问题)。您必须能够定义一个操作,该操作定义用于键中键类型的总顺序std::map,否则您的元素或位姿将只有部分顺序,其属性的A可能不等于B.在这种情况下通常会发生的事情是,您将能够插入键/值对,但是如果您遍历整个地图和/或检测到“缺失”,则可能会得到重复的键/值对。尝试std::map::find()在映射中执行特定键/值对的键/值对。


至于其他答案,无论如何,这实际上并不能回答我的问题。
Kiril Kirov

-3

begin()可以给出最小的元素。但这取决于实现。它在C ++标准中指定吗?如果没有,那么做这个假设是危险的。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.