QMap<QString, QVariant> someMap;Potrzebujemy wykonać jakąś operację na każdej wartości w tej QMapie. Zauważyłem u mojego kolegi z zespołu taki kod:
foreach(QString, someMap.keys()){...}Nigdy by mi to do głowy to nie przyszło, tak więc postanowiłem sprawdzić, czy rzeczywiście jest to takie złe rozwiązanie. Napisałem programik testowy, uwzględniający cztery przypadki:
46 QStringList tmp;To przypadek pierwszy, który zainspirował ten artykuł
47 for(auto i(0); i < LOOP_LENGTH; i++){
48 foreach(QString key, testMap.keys()){
49 tmp << testMap.value(key).toString();
50 }
51 }
59 QStringList tmp;Przypadek drugi różni się od pierwszego użyciem stałej referencji, zamiast tworzenia kopi QStringa
60 for(auto i(0); i < LOOP_LENGTH; i++){
61 foreach(const QString &key, testMap.keys()){
62 tmp << testMap.value(key).toString();
63 }
64 }
72 QStringList tmp;A tutaj kod który wydawał mi się najbardziej intuicyjny w takim przypadku - użycie iteratora. Jest jeszcze czwarty przypadek, z użyciem stałego iteratora. A teraz wyniki testu, przy kluczu o średniej długości 6-ciu znaków, 10-cio elementowej mapie oraz pętli wykonywanej 10000 razy:
73 for(auto i(0); i < LOOP_LENGTH; i++){
74 QMap::iterator it(testMap.begin());
75 while(it != testMap.end()){
76 tmp << it.value().toString();
77 ++it;
78 }
79 }
"Preparing QMap with 10 items. Key prefix length: 4 , value prefix length 6. loop length 10000"Jak wydać wersje używające stałego iteratora oraz stałej referencji są nieco szybsze od swoich modyfikowalnych odpowiedników. Wersja z iteratorem jest natomiast prawie 4 razy szybsza niż wersja z użyciem pętli foreach i metody QMap::keys(). A teraz teścik przy nieco zmienionych ustawieniach, wydłużamy rozmiar klucza oraz zwiększamy wielkość mapy.
Foreach test
Time elapsed: 48
Const foreach test
Time elapsed: 43
Iterator test
Time elapsed: 14
Const iterator test
Time elapsed: 13
"Preparing QMap with 1000 items. Key prefix length: 8 , value prefix length 6. loop length 10000"
Foreach test
Time elapsed: 7127
Const foreach test
Time elapsed: 6692
Iterator test
Time elapsed: 1378
Const iterator test
Time elapsed: 1241
Teraz różnica szybkości jest już prawie 6-cio krotna. Dlaczego? Zobaczmy co robi funkcja QMap::keys():
QListres;
res.reserve(size());
const_iterator i = begin();
while (i != end()) {
res.append(i.key());
++i;
}
I to wystarczy za cały komentarz. Dobrą praktyką jest zaglądnięcie do kodu Qt, zanim zrobimy jakąś głupotę. Dziękuję za uwagę :)