What is the preferred/idiomatic way to insert into a map?

前端 未结 9 1443
感动是毒
感动是毒 2020-11-28 18:45

I have identified four different ways of inserting elements into a std::map:

std::map function;

function[0] = 42;
function.inse         


        
相关标签:
9条回答
  • 2020-11-28 19:10

    In short, [] operator is more efficient for updating values because it involves calling default constructor of the value type and then assigning it a new value, while insert() is more efficient for adding values.

    The quoted snippet from Effective STL: 50 Specific Ways to Improve Your Use of the Standard Template Library by Scott Meyers, Item 24 might help.

    template<typename MapType, typename KeyArgType, typename ValueArgType>
    typename MapType::iterator
    insertKeyAndValue(MapType& m, const KeyArgType&k, const ValueArgType& v)
    {
        typename MapType::iterator lb = m.lower_bound(k);
    
        if (lb != m.end() && !(m.key_comp()(k, lb->first))) {
            lb->second = v;
            return lb;
        } else {
            typedef typename MapType::value_type MVT;
            return m.insert(lb, MVT(k, v));
        }
    }
    

    You may decide to choose a generic-programming-free version of this, but the point is that I find this paradigm (differentiating 'add' and 'update') extremely useful.

    0 讨论(0)
  • 2020-11-28 19:11

    The first version:

    function[0] = 42; // version 1
    

    may or may not insert the value 42 into the map. If the key 0 exists, then it will assign 42 to that key, overwriting whatever value that key had. Otherwise it inserts the key/value pair.

    The insert functions:

    function.insert(std::map<int, int>::value_type(0, 42));  // version 2
    function.insert(std::pair<int, int>(0, 42));             // version 3
    function.insert(std::make_pair(0, 42));                  // version 4
    

    on the other hand, don't do anything if the key 0 already exists in the map. If the key doesn't exist, it inserts the key/value pair.

    The three insert functions are almost identical. std::map<int, int>::value_type is the typedef for std::pair<const int, int>, and std::make_pair() obviously produces a std::pair<> via template deduction magic. The end result, however, should be the same for versions 2, 3, and 4.

    Which one would I use? I personally prefer version 1; it's concise and "natural". Of course, if its overwriting behavior is not desired, then I would prefer version 4, since it requires less typing than versions 2 and 3. I don't know if there is a single de facto way of inserting key/value pairs into a std::map.

    Another way to insert values into a map via one of its constructors:

    std::map<int, int> quadratic_func;
    
    quadratic_func[0] = 0;
    quadratic_func[1] = 1;
    quadratic_func[2] = 4;
    quadratic_func[3] = 9;
    
    std::map<int, int> my_func(quadratic_func.begin(), quadratic_func.end());
    
    0 讨论(0)
  • 2020-11-28 19:13

    I have been running some time comparisons between the abovementioned versions:

    function[0] = 42;
    function.insert(std::map<int, int>::value_type(0, 42));
    function.insert(std::pair<int, int>(0, 42));
    function.insert(std::make_pair(0, 42));
    

    Turns out that time differences between the insert versions are tiny.

    #include <map>
    #include <vector>
    #include <boost/date_time/posix_time/posix_time.hpp>
    using namespace boost::posix_time;
    class Widget {
    public:
        Widget() {
            m_vec.resize(100);
            for(unsigned long it = 0; it < 100;it++) {
                m_vec[it] = 1.0;
            }
        }
        Widget(double el)   {
            m_vec.resize(100);
            for(unsigned long it = 0; it < 100;it++) {
                m_vec[it] = el;
            }
        }
    private:
        std::vector<double> m_vec;
    };
    
    
    int main(int argc, char* argv[]) {
    
    
    
        std::map<int,Widget> map_W;
        ptime t1 = boost::posix_time::microsec_clock::local_time();    
        for(int it = 0; it < 10000;it++) {
            map_W.insert(std::pair<int,Widget>(it,Widget(2.0)));
        }
        ptime t2 = boost::posix_time::microsec_clock::local_time();
        time_duration diff = t2 - t1;
        std::cout << diff.total_milliseconds() << std::endl;
    
        std::map<int,Widget> map_W_2;
        ptime t1_2 = boost::posix_time::microsec_clock::local_time();    
        for(int it = 0; it < 10000;it++) {
            map_W_2.insert(std::make_pair(it,Widget(2.0)));
        }
        ptime t2_2 = boost::posix_time::microsec_clock::local_time();
        time_duration diff_2 = t2_2 - t1_2;
        std::cout << diff_2.total_milliseconds() << std::endl;
    
        std::map<int,Widget> map_W_3;
        ptime t1_3 = boost::posix_time::microsec_clock::local_time();    
        for(int it = 0; it < 10000;it++) {
            map_W_3[it] = Widget(2.0);
        }
        ptime t2_3 = boost::posix_time::microsec_clock::local_time();
        time_duration diff_3 = t2_3 - t1_3;
        std::cout << diff_3.total_milliseconds() << std::endl;
    
        std::map<int,Widget> map_W_0;
        ptime t1_0 = boost::posix_time::microsec_clock::local_time();    
        for(int it = 0; it < 10000;it++) {
            map_W_0.insert(std::map<int,Widget>::value_type(it,Widget(2.0)));
        }
        ptime t2_0 = boost::posix_time::microsec_clock::local_time();
        time_duration diff_0 = t2_0 - t1_0;
        std::cout << diff_0.total_milliseconds() << std::endl;
    
        system("pause");
    }
    

    This gives respectively for the versions (I ran the file 3 times, hence the 3 consecutive time differences for each):

    map_W.insert(std::pair<int,Widget>(it,Widget(2.0)));
    

    2198 ms, 2078 ms, 2072 ms

    map_W_2.insert(std::make_pair(it,Widget(2.0)));
    

    2290 ms, 2037 ms, 2046 ms

     map_W_3[it] = Widget(2.0);
    

    2592 ms, 2278 ms, 2296 ms

     map_W_0.insert(std::map<int,Widget>::value_type(it,Widget(2.0)));
    

    2234 ms, 2031 ms, 2027 ms

    Hence, results between different insert versions can be neglected (didn't perform a hypothesis test though)!

    The map_W_3[it] = Widget(2.0); version takes about 10-15 % more time for this example due to an initialization with the default constructor for Widget.

    0 讨论(0)
提交回复
热议问题