Итерация по QMap с помощью for - PullRequest
48 голосов
/ 15 декабря 2011

У меня есть QMap объект, и я пытаюсь записать его содержимое в файл.

QMap<QString, QString> extensions;
//.. 

for(auto e : extensions)
{
  fout << e.first << "," << e.second << '\n';
}  

Почему я получаю: error: 'class QString' has no member named 'first' nor 'second'

Разве e не относится к типу QPair?

Ответы [ 7 ]

45 голосов
/ 16 декабря 2011

Если вы хотите стиль STL с first и second, сделайте это:

for(auto e : extensions.toStdMap())
{
  fout << e.first << "," << e.second << '\n';
}

Если вы хотите использовать то, что предлагает Qt, сделайте это:

for(auto e : extensions.keys())
{
  fout << e << "," << extensions.value(e) << '\n';
}
42 голосов
/ 16 декабря 2011

C ++ 11 range-for использует тип разыменованного итератора в качестве автоматически выводимого типа «курсора».Здесь это тип выражения *map.begin().
И поскольку QMap::iterator::operator*() возвращает ссылку на значение (типа QString &), ключ не доступен с помощью этого метода.

Вам следует использовать один из методов итератора, описанных в документациино вы должны избегать использования

  • keys(), потому что это включает в себя создание списка ключей и затем поиск значения для каждого ключа, или,
  • toStdMap(), потому что он копирует всесопоставить элементы с другим,

, и это не будет оптимальным.


Вы также можете использовать оболочку, чтобы получить QMap::iterator в качестве типа auto:
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';
}

Здесь есть еще одна оболочка .

22 голосов
/ 05 июля 2015

Для людей, интересующихся оптимизацией, я попробовал несколько подходов, сделал несколько микро-тестов и могу заключить, что Подход в стиле STL значительно быстрее .

Я попытался добавить целые числа сэти методы:

  • QMap :: values ​​()
  • Итератор в стиле Java (как указано в документации)
  • Итератор в стиле STL (как рекомендуется в документации также)

И я сравнил это с суммированием целых чисел QList / QVector

Результаты:

Reference vector :   244  ms
Reference list :     1239  ms

QMap::values() :     6504  ms
Java style iterator :    6199  ms
STL style iterator :     2343  ms

Код для заинтересованных:

#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";
}

Редактирование, июль 2017 года: я снова запустил этот код на своем новом ноутбуке (Qt 5.9, i7-7560U) и получил несколько интересных изменений

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

Стиль STL и стиль Java имеют очень похожие характеристики в этомтест

17 голосов
/ 15 декабря 2011

QMap :: iterator использует key () и value () - что можно легко найти в документации для Qt 4.8 или в документации для Qt-5 .

Edit:

На основе диапазона for цикл генерирует коды, подобные этим (см. Ссылка CPP ):

{
    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 * () эквивалентен QMap :: iterator :: value () и не дает пары.

Лучший способ написать это без цикла for на основе диапазона:

auto end = extensions.cend();
for (auto it = extensions.cbegin(); it != end; ++it)
{
    std::cout << qPrintable(it.key()) << "," << qPrintable(it.value());
}
13 голосов
/ 15 декабря 2011

В "старом" C ++, используя Qt, вы сделали бы это так:

QMap< QString, whatever > extensions;
//...
foreach( QString key, extensions.keys() )
{
    fout << key << "," << extensions.value( key ) << '\n';
}

У меня нет компилятора C ++ 11 здесь, но, возможно, будет работать следующее:

for( auto key: extensions.keys() )
{
    fout << key << "," << extensions.value( key ) << '\n';
}

Вместо этого вы также можете использовать итераторы, посмотрите ссылку на hmuelners, если вы предпочитаете использовать их

1 голос
/ 26 августа 2016

Я использовал что-то подобное, чтобы достичь своего собственного результата.На всякий случай кому-то нужны ключи и значения отдельно.

{
   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 голосов
/ 15 марта 2017

Другой удобный метод из QMap Docs .Он разрешает явный доступ к ключу и значению (итератор в стиле Java):

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

Если вы хотите иметь возможность перезаписи, используйте QMutableMapIterator.

Есть еще один удобный *Метод 1009 *, если вас интересует только чтение значений, без ключей (используя Qt s foreach и c ++ 11):

QMap<QString, QString> extensions;
// ... fill extensions
foreach (const auto& value, extensions)
{
    // to stuff with value
}
...