5. QMultiHash
QMultiHash是QHash的子類,是用於處理多值映射的便利類,其用法與QMultiMap類似。
終於將容器給講解的差不多了,可以來到我們的重點——講解 迭代器 的使用
迭代器(iterator) 為訪問容器類裡的數據項提供了統一的方法,Qt 有兩種迭代器類: Java 類型的迭代器和STL類型的迭代器。
Java類型 的迭代器更易於使用,且提供一些高級功能,而STL類型的迭代器效率更高。
Qt 還提供一個關鍵字 foreach() (實際是 <QtGlobal\> 裡定義的一個宏) 用於方便地訪問容器裡所有數據項。
Java類型迭代器Java類型迭代器集合總覽java風格的迭代器是Qt 4中的新元素,是Qt應用程式中使用的標準迭代器。它們使用起來比STL風格的迭代器更方便,但效率稍低一些。它們的API模仿Java的迭代器類。
對於每個容器類 ,有兩個Java類型迭代器:一個用於只讀操作,一個用於讀寫操作,各個Java類型 的容器類見下表.
容器類只讀迭代器讀寫迭代器QList<T\>, QQueue<T\>QListIterator<T\>QMutableListIterator<T\>QLinkedList<T\>QLinkedListIterator<T\>QMutableLinkedListIterator<T\>QVector<T\>, QStack <T\>QVectorIterator<T\>QMutableVectorIterator<T\>QSet<T\>QSetIterator<T\>QMutableSetIterator<T\>QMap, QMultiMapQMapIteratorQMutableMapIteratorQHash, QMultiHashQHashIteratorQMutableHashIteratorQMap和QHash等關聯容器類的迭代器用法相同,QList和QLinkedList、QSet等容器類的用法相同,所以下面只以QMap和QList為例介紹迭代器的用法。
Java類型有序容器類的迭代器的使用 Java類型迭代器的指針不是指向一個數據項,而是在數據項之間,迭代器指針位置示意圖如下圖所示。
QList<QString> list;
list << "A" << "B" << "C" << "D";
QListIterator<QString> i(list);
while (i.hasNext())
qDebug() << i.next();
QList<QString\> 容器對象list作為參數傳遞給 QListIterator< QString > 迭代器i 的構造函數,i 用於對 list 作只讀遍歷。起始時刻,迭代器指針在容器第一個數據項的前面(上圖中 數據項A 的前面),調用 hasNext() 判斷在迭代器指針後面是否還有數據項,如果有,就調用 next() 跳過一個數據項,並且 next() 函數返回跳過去的那個數據項。
也可以反向遍歷,示例代碼如下:
QListIterator<QString> i (list) ;
i.toBack();
while (i.hasPrevious())
qDebug() << i.previous () ;
該代碼與前向迭代是對稱的,除了我們首先調用toBack()將迭代器移動到列表中最後一項之後。
下面的圖表說明了在迭代器上調用 next() 和 previous() 的效果:
QListIterator 用於移動指針和讀取數據的函數見下表:
函數名功能描述void toFront()迭代器移動到列表的最前面(第一個數據項之前)void toBack()迭代器移動到列表的最後面(最後一個數據項之後)bool hasNext()如果迭代器不是位於列表最後位置,返回trueconst T & next()返回下一個數據項,並且迭代器後移-一個位置const T & peekNext()返回下一個數據項,但是不移動迭代器位置bool hasPrevious()如果迭代器不是位於列表的最前面,返回trueconst T & previous()返回前一個數據項,並且迭代器前移-一個位置const T & peekPrevious()返回前一個數據項,但是不移動迭代器指針QListIterator是只讀訪問容器內數據項的迭代器,若要在遍歷過程中對容器的數據進行修改,需要使用QMutableListIterator。例如下面的示例代碼為刪除容器中數據為奇數的項。
QList<int> list;
list <<1 <<2<<3<<4<<5;
QMutableListIterator<int> i(list);
while (i.hasNext()){
if(i.next()%2 != 0)
i. remove();
}
remove() 函數移除next)函數剛剛跳過的一一個 數據項,不會使迭代器失效。setValue() 函數可以修改剛剛跳過去的數據項的值。
Java類型關聯容器類的迭代器的使用 對於關聯容器類QMap<Key T\>,使用QMapIterator 和QMutableMapIterator迭代器類,它們具有前面表中所示的所有函數,主要是增加了 key()和value() 函數用於獲取剛剛跳過的數據項的鍵和值。
例如,下面的代碼將刪除鍵(城市名稱)裡以City結尾的數據項。
QMap<QString,QString> map;
map.insert("Paris", "France");
map.insert("New York", "USA");
map.insert("Mexico City", "USA");
map.insert("Moscow", "Russia");
QMutableMapIterator<QString, QString> i(map);
while (i.hasNext()){
if (i.next().key().endsWith("City"))
i.remove() ;
}
如果是在多值容器裡遍歷,可以用findNext()或findPrevious()查找下一個或上一個值,如下面的代碼將刪除上一示例代碼中 map 裡值為 USA 的所有數據項。
QMutableMapIterator<QString, QString> i(map);
while (i.findNext("USA"))
i.remove();
STL風格的迭代器在Qt 2.0發布後就可用了。它們與Qt和STL的通用算法兼容,並針對速度進行了優化
對於每一個容器類,都有兩個STL類型迭代器: 一個用於只讀訪問,一個用於讀寫訪問。無需修改數據時一定使用只讀迭代器,因為它們速度更快。具體類型見下表:
容器類只讀迭代器讀寫迭代器QList<T\>, QQueue<T\>QList<T\>::const_iteratorQList<T\>::iteratorQLinkedList<T\>QLinkedList<T\> :const_iteratorQLinkedList<T\>::iteratorQVector<T\>, QStack<T\>QVector<T\>::const_iteratorQVector<T\> :iteratorQSet<T\>QSet<T\>::const_iteratorQSet<T\>::iteratorQMap,QMultiMapQMap:const_iteratorQMap::iteratorQHash,QMultiHashQHash::const_iteratorQHash::iterator注意:在定義只讀迭代器和讀寫迭代器時的區別, 它們使用了不同的關鍵字,const_iterator 定義只讀迭代器,iterator定義讀寫迭代器。此外,還可以使用const_reverse_iterator 和reverse_iterator 定義相應的反向迭代器。
STL類型 的迭代器是數組的指針,所以 ++ 運算符使迭代器指向下一個數據項,* 運算符返回數據項內容。與Java類型 的迭代器不同,STL 迭代器 直接指向數據項,STL 迭代器指向位置示意圖如下圖所示。
begin() 函數使迭代器指向容器的第一個數據項,end() 函數使迭代器指向一個虛擬的表示結尾的數據項,end() 表示的數據項是無效的,一般用作循環結束條件。
下面仍然以QList和QMap為例說明STL迭代器的用法,其他容器類迭代器的用法類似。
下面的示例代碼將QList<QString\> list 裡的數據項逐項輸出。
iterator 用法
QList<QString> list;
list << "A" << "B" << "C" << "D";
QList<QString>::iterator i;
for (i = list.begin(); i != list.end(); ++i)
*i = (*i).toLower();
const_iterator 用法
QList<QString> list;
list << "A" << "B" <<"C" << "D";
QList<QString>::const_iterator i;
for (i = list.constBegin() ; i != list.constEnd(); ++i)
qDebug() << *i;
constBegin()和constEnd() 是用於只讀迭代器的,表示起始和結束位置。若使用反向讀寫迭代器,並將上面示例代碼中list 的數據項都改為小寫,代碼如下:
QList<QString>::reverse_iterator i;
for (i = list.rbegin(); i != list.rend(); ++i)
{
*i = i->toLower () ;
}
對於關聯容器類QMap和QHash,迭代器的 * 操作符返回數據項的值。如果想返回鍵,使用key() 函數。對應的,用 value() 函數返回一個項的值。
例如,下面的代碼將QMapmap中所有項的鍵和值輸出:
QMap<int,int> map;
QMap<int,int>::const_iterator i;
for (i = map.constBegin() ; i != map.constEnd() ; ++i)
qDebug() << i.key() << ":"<< i.value();
QtAPI包含很多返回值為QList或QStringList的函數,要遍歷這些返回的容器,必須先複製。由於Qt使用了隱式共享,這樣的複製並無多大開銷。例如下面的代碼是正確的。
const QList<int> sizes = splitter->sizes() ;
QList<int>::const_iterator i;
for (i = sizes.begin(); i != sizes.end(); ++i)
...
隱式共享 ( Implicit Sharing) 是對象的管理方法。一個對象被隱式共享,只是傳遞該對象的一個指針給使用者,而不實際複製對象數據,只有在使用者修改數據時,才實質複製共享對象給使用者。如在上面的代碼中,splitter->sizes()返回的是一個QList<int\>列表對象sizes, 但是實際上代碼並不將splitter->sizes()表示的列表內容完全複製給變量sizes,只是傳遞給它一個指針。只有當sizes 發生數據修改時,才會將共享對象的數據複製給sizes,這樣避免了不必要的複製,減少了資源佔用。
而下面的代碼是錯誤的。
QList<int>::const_iterator i;
for (i = splitter->sizes().begin(); i != splitter->sizes().end(); ++i)
...
對於STL類型的迭代器,隱式共享還涉及另外一個問題,即當有- -個迭代器在操作- - 個容器變量時,不要去複製這個容器變量。
foreach關鍵字目前foreach已經可以被C++11 標準庫for循環代替了。所以代碼實用時不建議使用foreach,直接使用for即可,下面還是介紹下foreach的大概用法
如果只是想遍歷容器中所有的項,可以使用 foreach 關鍵字。foreach 是<QtGlobal\>頭文件中定義的一個宏。使用foreach的句法是:
foreach(variable,container )
使用foreach的代碼比使用迭代器更簡潔。例如,使用foreach 遍歷一個QStringList的示例代碼如下:
QStringList list;
list<<"A"<<"B"<<"C";
QString str;
foreach (str, list)
qDebug() < str;
替代的C++11for循環用法,(僅限C++11)
for(str:list)
qDebug() < str;
輸出結果:A,B,C
用於迭代的變量也可以在foreach語句裡定義,foreach 語句也可以使用花括號,可以使用break 退出迭代,示例代碼如下:
QStringList strlist;
strlist<<"A"<<"B"<<"C";
foreach (const QString &str, strlist) {
if (str. isEmpty())
break;
qDebug() << str;
}
對於QMap和QHash,foreach 會自動訪問 鍵一一值 對裡的值,所以無需調用values()。 如果需要訪問鍵則可以調用keys() ,示例代碼如下:
QMap<QString,int> map;
foreach (const QString &str, map.keys() )
qDebug() << str << ":" << map.value(str) ;
注意: foreach 關鍵字遍歷一個容器變量是創建了容器的一個副本,所以不能修改原來容器變量的數據項。
到這裡,容器,迭代器的使用就全部講解完畢了!又不懂得隨時評論留言!
Qt/Qml零基礎入門系列文章總綱