使用for迭代QMap

use*_*635 50 c++ qt qmap c++11

我有一个QMap对象,我正在尝试将其内容写入文件.

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

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

为什么我得到: error: 'class QString' has no member named 'first' nor 'second'

e不是类型QPair

Arl*_*len 48

如果你想要STL样式firstsecond,请执行以下操作:

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

如果您想使用Qt提供的功能,请执行以下操作:

for(auto e : extensions.keys())
{
  fout << e << "," << extensions.value(e) << '\n';
}
Run Code Online (Sandbox Code Playgroud)

  • toStdMap()和keys()方法将复制地图的全部内容或它的密钥,这是非常资源丰富的.我想知道为什么它被选为最佳答案并获得如此多的选票 (46认同)
  • 因为它有效并且看起来不错. (4认同)
  • 简而言之,Qt 用户。 (3认同)
  • @FPGAwarrior相反,它[似乎](https://code.woboq.org/qt5/qtbase/src/corelib/tools/qmap.h.html#_ZNK4QMap8toStdMapEv)`toStdMap()`的大小是线性的`QMap`,所以*应该*有所作为. (2认同)
  • @FPGAwarrior谁说“QMap”在内部将键存储在“QList”中?我敢打赌这个列表是根据请求根据地图的内部结构构建的。 (2认同)
  • @AdrienLeravat 更新:这些天 `qAsConst` 正在被标准的 `std::as_const` 替换。 (2认同)

ale*_*sdm 47

C++ 11 range-based-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';
}
Run Code Online (Sandbox Code Playgroud)

还有另一种包装这里.


Fez*_*vez 24

对于对优化感兴趣的人,我尝试了几种方法,做了一些微观基准测试,我可以得出结论,STL风格方法明显更快.

我试过用这些方法添加整数:

  • QMAP ::值()
  • 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
Run Code Online (Sandbox Code Playgroud)

感兴趣的代码:

#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";
}
Run Code Online (Sandbox Code Playgroud)

编辑2017年7月:我在我的新笔记本电脑(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
Run Code Online (Sandbox Code Playgroud)

STL风格和Java风格在此基准测试中具有非常相似的性能

  • 这是最好的答案,值得选择.每个人都可以在他们的系统上轻松测试你说的是实话.我的值与你的值非常相似,我想知道为什么stl迭代器几乎快了两倍.Qt文档误导性地说"[Java样式迭代器]它们比STL样式的迭代器更方便使用,但代价是效率稍低." 100%略高于imo (6认同)

hmu*_*ner 18

QMap :: iterator使用key()和value() - 可以在Qt 4.8文档Qt-5文档中轻松找到.

编辑:

基于范围的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';
    }
} 
Run Code Online (Sandbox Code Playgroud)

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());
}
Run Code Online (Sandbox Code Playgroud)


Tim*_*yer 13

在"旧"C++中,使用Qt,你会这样做:

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

我这里没有C++ 11编译器,但以下可能会有效:

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

您也可以使用迭代器,如果您更喜欢使用它们,请查看hmuelners链接

  • 我怀疑这是最有效的"旧"方式.更有可能:`for(QMap <QString,what> :: iterator it = map.begin(); it!= map.end(); it ++){...}` (4认同)
  • 感谢您在"旧"C++中执行此操作的参考.我们中的一些人被困在老狗的狗窝里;-) (2认同)