STL地图插入效率:[]与插入

时间:2011-04-17 07:23:57

标签: c++ stl map insert std-pair

地图插入有两种方式:

m[key] = val;

或者

m.insert(make_pair(key, val));

我的问题是,哪种操作更快? 人们通常说第一个比较慢,因为如果'key'在map中不存在,STL Standard首先'插入'一个默认元素,然后将'val'分配给默认元素。

但由于'make_pair',我没有看到第二种方式更好。与pair<T1, T2>(key, val)相比,make_pair实际上是一种方便的“配对”方式。无论如何,他们都做了两个任务,一个是将'key'分配给'pair.first',两个分配'val'给'pair.second'。完成配对后,map会插入由'pair.second'初始化的元素。

所以第一种方式是1.'default construct of typeof(val)'2。赋值 第二种方式是1.赋值2.'copy construct of typeof(val)'

6 个答案:

答案 0 :(得分:17)

两者都完成了不同的事情。

m[key] = val;

如果key不存在,则会插入新的键值对,否则它将覆盖映射到key的旧值(如果已存在)。

m.insert(make_pair(key, val));

如果key尚不存在,只会插入该对,它将永远不会覆盖旧值。因此,请根据您想要完成的任务进行选择 对于更有效的问题:个人资料。 :P可能是我说的第一种方式。赋值(又名副本)是两种方式的情况,所以唯一的区别在于构造。众所周知并且应该实现,默认构造基本上应该是无操作,因此非常有效。副本就是 - 副本。因此,我们得到一个“无操作”和一个副本,然后两个我们得到两个副本 修改:最后,相信您的分析告诉您的内容。我的分析就像@Matthieu在他的评论中提到的那样,但那是我的猜测。 :)


然后,我们有C ++ 0x,而第二种方式的双重拷贝将是无效的,因为现在可以简单地移动该对。所以最后,我认为它回到了我的第一点:用正确的方式来完成你想要做的事情。

答案 1 :(得分:5)

在内存充足的轻载系统上,此代码为:

#include <map>
#include <iostream>
#include <ctime>
#include <string>

using namespace std;

typedef map <unsigned int,string> MapType;
const unsigned int NINSERTS = 1000000;

int main() {
    MapType m1;
    string s = "foobar";
    clock_t t = clock();
    for ( unsigned int i = 0; i < NINSERTS; i++ ) {
        m1[i] = s;
    }
    cout << clock() - t << endl;
    MapType m2;
    t = clock();
    for ( unsigned int i = 0; i < NINSERTS; i++ ) {
        m2.insert( make_pair( i, s ) );
    }
    cout << clock() - t << endl;
}

产生

1547
1453

或重复运行时的类似值。因此插入(在这种情况下)略快一些。

答案 2 :(得分:2)

表现明智我认为它们大致相同。对于具有大对象的地图,可能存在一些例外情况,在这种情况下,您应该使用[]或者使用emplace来创建比插入&#39;更少的临时对象。有关详细信息,请参阅讨论here

但是,如果您使用“提示”,则可以在特殊情况下获得性能提升。插入操作符上的函数。因此,从2查看选项here

iterator insert (const_iterator position, const value_type& val);

&#39;插入&#39;如果你提供了一个很好的提示,那么操作可以减少到恒定的时间(从log(n)开始)(如果你知道你在地图后面添加了东西的情况下通常会这样)。

答案 3 :(得分:0)

我们必须通过提及相对性能取决于被复制对象的类型(大小)来改进分析。

我用(int - &gt; set)的地图做了类似的实验(到nbt)。我知道这是一件可怕的事情,但是,这个场景的说明。 “值”,在这种情况下是一组整数,其中包含20个元素。

我执行了[] = Vs的一百万次迭代。插入操作并执行RDTSC / iter-count。

[] =设置| 10731次循环

insert(make_pair&lt;&gt;)| 26100次循环

它显示了由于复制而增加的惩罚幅度。当然,CPP11(移动ctor's)  会改变画面。

答案 4 :(得分:0)

我的看法: 值得提醒的是,map是一个平衡的二叉树,大多数修改和检查都需要O(logN)。

真正取决于您要解决的问题。

1)如果您只想插入值,知道它还没有, 那么[]会做两件事: a)检查物品是否存在 b)如果不存在将会创建对并执行插入操作(    O(logN)的双重工作,所以我会使用insert。

2)如果您不确定它是否存在,那么a)如果您通过执行if(map.find(item)== mp.end())之类的操作来检查项目是否在那里在某处上面的行,然后使用insert,因为双重工作[]将执行b)如果你没有检查,那么它取决于,因为插入赢得修改值,如果它在那里,[]将,否则他们是平等的

答案 5 :(得分:0)

我的答案不是效率,而是安全性,这与选择插入算法有关:

[]insert()调用将触发元素的析构函数。如果您的析构函数内部有严重的行为,则这可能会带来危险的副作用。

发生此类危险之后,我不再依赖STL的隐式惰性插入功能,并始终使用显式检查对象是否在其ctor / dtor中具有行为。

看到以下问题: Destructor called on object when adding it to std::list