STL 映射插入效率:[] 与插入



有两种映射插入方式:

m[key] = val;

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

我的问题是,哪种操作更快?人们通常说第一个速度较慢,因为如果map中不存在"key",STL标准首先"插入"默认元素,然后将"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) '

两者都完成不同的事情。

m[key] = val;

如果key尚不存在,则将插入新的键值对,或者如果已存在,它将覆盖映射到key的旧值。

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

仅当key尚不存在时才插入该对,它永远不会覆盖旧值。因此,请根据您想要完成的内容进行相应的选择。
对于问题,什么更有效:配置文件。:P 可能是我要说的第一种方式。两种方式都是赋值(又名复制(,所以唯一的区别在于构造。众所周知,应该实现,默认构造基本上应该是无操作的,因此非常有效。副本就是这样 - 副本。因此,在第一种方式中,我们得到一个"无操作"和一个副本,在第二种方式中,我们得到两个副本。
编辑:最后,相信您的分析告诉您的内容。我的分析就像他评论中提到的@Matthieu一样,但这是我的猜测。:)


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

在具有大量内存的轻负载系统上,以下代码:

#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

或重复运行的类似值。所以插入(在这种情况下(稍微快一点。

性能方面,我认为它们总体上是相同的。对于具有大型对象的地图,可能会有一些例外,在这种情况下,您应该使用 [] 或 emplace,它创建的临时对象少于"insert"。有关详细信息,请参阅此处的讨论。

但是,如果您在插入运算符上使用"提示"功能,则在特殊情况下可能会提高性能。因此,从这里查看选项 2:

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

如果您给出一个很好的提示,"插入"操作可以减少到恒定时间(从 log(n(((如果您知道要在地图后面添加内容,则通常是这种情况(。

我们必须通过

提到相对性能也取决于被复制对象的类型(大小(来完善分析。

我用(int -> set(的地图做了一个类似的实验(对nbt(。我知道这是一件可怕的事情,但是,说明这种情况。"值",在本例中为一组整数,其中包含 20 个元素。

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

[] = 设置 | 10731 次循环

插入(make_pair<>( | 26100 次循环

它显示了由于复制而增加的惩罚程度。当然,CPP11(移动ctor的( 将改变图片。

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

实际上取决于您要解决的问题。

1(如果您只想插入值,知道它尚不存在,然后 [] 将做两件事:a( 检查物品是否存在b( 如果不存在,将创建对并执行插入所做的事情( O(logN( 的双重功(,所以我会使用插入。

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

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

[]insert()调用将触发元素的析构函数。这可能会产生危险的副作用,例如,如果您的析构函数内部有关键行为。

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

看到这个问题:将对象添加到 std::list 时对对象调用析构函数

最新更新