细节改善效率:c++中map结构erase函数不同用法的效率差异

本文通过实验对比了C++标准库中map容器三种erase方法的性能:通过迭代器erase、通过键值erase以及直接通过键值erase。实验结果显示,通过迭代器erase的方法效率最高。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

readme

c++标准库中map容器的增删改查操作十分常见,这篇博客对map容器的“删”动作做了较为详细的测试,“删”有不止一种方法,效率是有差异的。

测试程序

//common.h
#ifndef _COMMON_H_
#define _COMMON_H_

#include <string>
#include <stdio.h>
#include <set>
#include <memory>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <iostream>
#include <utility>
#include <algorithm>
#include <map>
#include <numeric>
#include <vector>
#include <iostream>
#include <iterator>
#include <sys/time.h>

using namespace std;
#endif
//Timer.h
#ifndef _TIMER_H
#define _TIMER_H

#include <sys/time.h>
#include "common.h"

class Timer
{
public:
    Timer(string strCallInfo = "Timer")
    {
        _desc = strCallInfo;
        gettimeofday(&_begin,NULL);
        gettimeofday(&_end, NULL);
    };

    ~Timer()
    {
        gettimeofday(&_end,NULL);
        printf("%s, cost %ldms\n", _desc.c_str(), (_end.tv_sec - _begin.tv_sec)*1000 + (_end.tv_usec - _begin.tv_usec)/1000);
    };
private:
    Timer(){};
    string  _desc;

    timeval _begin;
    timeval _end;
};

#endif
//map_erase.cpp
#include "common.h"
#include "Timer.h"

void asseble_map(map<string, int> &stringIntMap){
    stringIntMap.clear();
    for (int i = 0; i < 1000000; ++i){
        char key_char[64];
        sprintf(key_char, "%d_key", i);
        string keyString(key_char);
        stringIntMap[keyString] = i;
    }
}

int main(void){
    map<string, int> stringIntMap;
    vector<string> stringVector;
    int i = 0;

    asseble_map(stringIntMap);
    for (i = 0; i < 1000000; ++i){
        char key_char[16];
        sprintf(key_char, "%d_key", i + 500000);
        string key_string(key_char);
        stringVector.push_back(key_string);
    }

    {
        Timer timer("erase_iter_after_find");
        for (i = 0; i < 1000000; ++i){
            std::map<string, int>::iterator pos = stringIntMap.find(stringVector[i]);
            if (pos != stringIntMap.end())
            {
                stringIntMap.erase(pos);
            }
        }
    }

    asseble_map(stringIntMap);
    {
        Timer timer("erase_key_after_find");
        for (i = 0; i < 1000000; ++i){
            std::map<string, int>::iterator pos = stringIntMap.find(stringVector[i]);
            if (pos != stringIntMap.end())
            {
                stringIntMap.erase(stringVector[i]);
            }
        }
    }

    asseble_map(stringIntMap);
    {
        Timer timer("erase_key_directly");
        for (i = 0; i < 1000000; ++i){
            stringIntMap.erase(stringVector[i]);
        }
    }
    return 0;

}

编译

g++ -o map_erase map_erase.cpp -O0 -g -Wall -std=c++11

执行

[root@***]# ./map_erase 
erase_iter_after_find, cost 628ms
erase_key_after_find, cost 1056ms
erase_key_directly, cost 847ms

总结

c++中map结构是十分常用的数据结构,其erase函数的官方定义中有三种重载,分别为:

iterator  erase (const_iterator position);
size_type erase (const key_type& k);
iterator  erase (const_iterator first, const_iterator last);

其中第三种重载用于删除两个迭代器之间的所有元素,在此不做讨论。
前两种都用于删除某一对kv,用法很相似,区别在于传入的参数是迭代器还是k本身。
第一种方法(erase_iter_after_find)需要先找到目标k在map结构中的迭代器,如果目标k不在map中,则找不到对应的迭代器,不可进行erase操作。如果目标k存在于map中,erase返回一个迭代器,指向被删除迭代器后一个迭代器。
第二种方法(erase_key_after_find)与第一种方法类似,区别在于传入的参数是k本身。
第三种方法(erase_key_directly)更为简单随意一些,直接传入一个k即可,不管目标k在map中存在与否都可以正确执行,erase返回被删除元素的数量。
经过测试,第一种方法效率最高。究其原因,可以分为两部分,下面进行详细解析。
第一部分,删除map中的元素必须要先找到该元素在map中的位置,这三种方法都进行了查找,其中第一种方法一开始就进行了查找,并且仅仅查找了一次;第二种方法一开始也进行了查找,但是传入的参数是k本身,erase的时候还会再一次进行一次内部隐式查找,一共查找了两次;第三种方法在erase内部隐式的进行查找,查找了一次。
第二部分,如上文所属,第一种方法和第三种方法都仅仅查找了一次,第一种方法更高效的原因是什么呢?erase操作最终的目标是删除迭代器,第三种方法需要有一步生成一个迭代器,构造迭代器的消耗产生了这两种方法的效率差异。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值