Iterating over a QMap with for

前端 未结 9 1690
执笔经年
执笔经年 2021-01-31 07:02

I\'ve a QMap object and I am trying to write its content to a file.

QMap extensions;
//.. 

for(auto e : extensions)
{
  fou         


        
相关标签:
9条回答
  • 2021-01-31 07:41

    C++11 range-based-for uses the type of the dereferenced iterator as the automatically deduced "cursor" type. Here, it is the type of the expression *map.begin().
    And since QMap::iterator::operator*() returns a reference to the value (of type QString &), the key isn't accessible using that method.

    You should use one of the iterator methods described in the documentation but you should avoid using

    • keys() because it involves creating a list of keys and then searching the value for each key, or,
    • toStdMap() because it copies all the map elements to another one,

    and that wouldn't be very optimal.


    You could also use a wrapper to get QMap::iterator as the auto type:

    template<class Map>
    struct RangeWrapper {
        typedef typename Map::iterator MapIterator;
        Map &map;
    
        RangeWrapper(Map & map_) : map(map_) {}
    
        struct iterator {
            MapIterator mapIterator;
            iterator(const MapIterator &mapIterator_): mapIterator(mapIterator_) {}
            MapIterator operator*() {
                return mapIterator;
            }
            iterator & operator++() {
                ++mapIterator;
                return *this;
            }
            bool operator!=(const iterator & other) {
                return this->mapIterator != other.mapIterator;
            }
        };
        iterator begin() {
            return map.begin();
        }
        iterator end() {
            return map.end();
        }
    };
    
    // Function to be able to use automatic template type deduction
    template<class Map>
    RangeWrapper<Map> toRange(Map & map)
    {
        return RangeWrapper<Map>(map);
    }
    
    // Usage code
    QMap<QString, QString> extensions;
    ...
    for(auto e : toRange(extensions)) {
        fout << e.key() << "," << e.value() << '\n';
    }
    

    There is another wrapper here.

    0 讨论(0)
  • 2021-01-31 07:45

    For people interested in optimizations, I have tried several approaches, did some micro benchmarks, and I can conclude that STL style approach is significantly faster.

    I have tried adding integers with these methods :

    • QMap::values()
    • Java style iterator (as advised in the documentation)
    • STL style iterator (as advised in the documentation too)

    And I have compared it with summing integers of a QList/QVector

    Results :

    Reference vector :   244  ms
    Reference list :     1239  ms
    
    QMap::values() :     6504  ms
    Java style iterator :    6199  ms
    STL style iterator :     2343  ms
    

    Code for those interested :

    #include <QDateTime>
    #include <QMap>
    #include <QVector>
    #include <QList>
    #include <QDebug>
    
    void testQMap(){
        QMap<int, int> map;
        QVector<int> vec;
        QList<int> list;
    
        int nbIterations = 100;
        int size = 1000000;
        volatile int sum = 0;
    
        for(int i = 0; i<size; ++i){
            int randomInt = qrand()%128;
            map[i] = randomInt;
            vec.append(randomInt);
            list.append(randomInt);
        }
    
    
        // Rererence vector/list
        qint64 start = QDateTime::currentMSecsSinceEpoch();
        for(int i = 0; i<nbIterations; ++i){
            sum = 0;
            for(int j : vec){
                sum += j;
            }
        }
        qint64 end = QDateTime::currentMSecsSinceEpoch();
        qDebug() << "Reference vector : \t" << (end-start) << " ms";
    
        qint64 startList = QDateTime::currentMSecsSinceEpoch();
        for(int i = 0; i<nbIterations; ++i){
            sum = 0;
            for(int j : list){
                sum += j;
            }
        }
        qint64 endList = QDateTime::currentMSecsSinceEpoch();
        qDebug() << "Reference list : \t" << (endList-startList) << " ms";
    
        // QMap::values()
        qint64 start0 = QDateTime::currentMSecsSinceEpoch();
        for(int i = 0; i<nbIterations; ++i){
            sum = 0;
            QList<int> values = map.values();
            for(int k : values){
                sum += k;
            }
        }
        qint64 end0 = QDateTime::currentMSecsSinceEpoch();
        qDebug() << "QMap::values() : \t" << (end0-start0) << " ms";
    
    
        // Java style iterator
        qint64 start1 = QDateTime::currentMSecsSinceEpoch();
        for(int i = 0; i<nbIterations; ++i){
            sum = 0;
            QMapIterator<int, int> it(map);
            while (it.hasNext()) {
                it.next();
                sum += it.value();
            }
        }
        qint64 end1 = QDateTime::currentMSecsSinceEpoch();
        qDebug() << "Java style iterator : \t" << (end1-start1) << " ms";
    
    
        // STL style iterator
        qint64 start2 = QDateTime::currentMSecsSinceEpoch();
        for(int i = 0; i<nbIterations; ++i){
            sum = 0;
            QMap<int, int>::const_iterator it = map.constBegin();
            auto end = map.constEnd();
            while (it != end) {
                sum += it.value();
                ++it;
            }
        }
        qint64 end2 = QDateTime::currentMSecsSinceEpoch();
        qDebug() << "STL style iterator : \t" << (end2-start2) << " ms";
    
    
        qint64 start3 = QDateTime::currentMSecsSinceEpoch();
        for(int i = 0; i<nbIterations; ++i){
            sum = 0;
            auto end = map.cend();
            for (auto it = map.cbegin(); it != end; ++it)
            {
                sum += it.value();
            }
        }
        qint64 end3 = QDateTime::currentMSecsSinceEpoch();
    
        qDebug() << "STL style iterator v2 : \t" << (end3-start3) << " ms";
    }
    

    Edit July 2017 : I ran this code again on my new laptop (Qt 5.9, i7-7560U) and got some interesting changes

    Reference vector :   155  ms 
    Reference list :     157  ms
    QMap::values():      1874  ms 
    Java style iterator: 1156  ms 
    STL style iterator:  1143  ms
    

    STL style and Java style have very similar performances in this benchmark

    0 讨论(0)
  • 2021-01-31 07:47

    Another convenient method, from the QMap Docs. It allows explicit access to key and value (Java-Style iterator):

    QMap<QString, QString> extensions;
    // ... fill extensions
    QMapIterator<QString, QString> i(extensions);
    while (i.hasNext()) {
        i.next();
        qDebug() << i.key() << ": " << i.value();
    }
    

    In case you want to be able to overwrite, use QMutableMapIterator instead.

    There's another convenient Qt method, if you're only interested in reading the values, without the keys (using Qts foreach and c++11):

    QMap<QString, QString> extensions;
    // ... fill extensions
    foreach (const auto& value, extensions)
    {
        // to stuff with value
    }
    
    0 讨论(0)
  • 2021-01-31 07:49

    QMap::iterator uses key() and value() - which can be found easily in the documentation for Qt 4.8 or the documentation for Qt-5.

    Edit:

    A range-based for loop generates codes similar to this (see CPP reference):

    {
        for (auto __begin = extensions.begin(), __end = extensions.end();
                __begin != __end; ++__begin) {
            auto e = *__begin; // <--- this is QMap::iterator::operator*()
            fout << e.first << "," << e.second << '\n';
        }
    } 
    

    QMap::iterator::iterator*() is equivalent to QMap::iterator::value(), and does not give a pair.

    The best way to write this is without range-based for loop:

    auto end = extensions.cend();
    for (auto it = extensions.cbegin(); it != end; ++it)
    {
        std::cout << qPrintable(it.key()) << "," << qPrintable(it.value());
    }
    
    0 讨论(0)
  • 2021-01-31 07:53

    Ivan Čukić of KDAB has a blog post that explains how to iterate over a QMap with C++17 structured bindings without copying the container:

    template <typename T>
    class asKeyValueRange
    {
    public:
        asKeyValueRange(T &data) : m_data{data} {}
    
        auto begin() { return m_data.keyValueBegin(); }
    
        auto end() { return m_data.keyValueEnd(); }
    
    private:
        T &m_data;
    };
    
    ...
    
    QMap<QString, QString> extensions;
    
    for (auto [key, value]: asKeyValueRange(extensions))
    {
        fout << key << ',' << value << '\n';
    } 
    
    0 讨论(0)
  • 2021-01-31 07:54

    I used something like this, to achieve my own result. Just in case someone needed the keys and values separately.

    {
       QMap<int,string> map; 
    
       map.insert(1,"One");
       map.insert(2,"Two");
       map.insert(3,"Three");
       map.insert(4,"Four");   
    
       fout<<"Values in QMap 'map' are:"<<endl;
       foreach(string str,map)
       {
         cout<<str<<endl;
       };
    
    
       fout<<"Keys in QMap 'map' are:"<<endl;
       foreach(int key,map.keys())
       {
         cout<<key<<endl;
       }; 
    }  
    
    0 讨论(0)
提交回复
热议问题