QT5容器遍歷
Qt 的容器類提供了兩種風格的遍歷器:Java 風格和 STL 風格。這兩種風格的遍歷器在通過非 const 函式對集合進行修改時都是不可用的。
Java 風格的遍歷器
Java 風格的遍歷器是在 Qt4 首先引入的,是 Qt 應用程式首先推薦使用的形式。這種風格比起 STL 風格的遍歷器更方便。方便的代價就是不如後者高效。它們的 API 非常類似於 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<Key, T> ,QMultiMap<Key,
T> |
QMapIterator<T> |
QMutableMapIterator<T> |
QHash<Key, T> ,QMultiHash<Key,
T> |
QHashIterator<T> |
QMutableHashIterator<T> |
這裡我們只討論QList
和QMap
的遍歷器。QLinkedList
、QVector
和QSet
的遍歷器介面與QList
的是一樣的;QHash
遍歷器的介面則同QMap
是一樣的。
不同於下面我們將要介紹的 STL 風格的遍歷器,Java 風格的遍歷器指向的是兩個元素之間的位置,而不是指向元素本身。因此,它們可能會指向集合第一個元素之前的位置,也可能指向集合的最後一個元素之後的位置,如下圖所示:
我們通過下面的程式碼看看如何使用這種遍歷器:
首先,我們使用 list 物件建立一個遍歷器。剛剛建立完成時,該遍歷器位於第一個元素之前(也就是 A 之前)。我們通過呼叫hasNext()
函式判斷遍歷器之後的位置上有無元素。如果有,呼叫next()
函式將遍歷器跳過其後的元素。next()
函式返回剛剛跳過的元素。當然,我們也可以使用hasPrevious()
和previous()
函式來從尾部開始遍歷,詳細內容可以參考 API 文件。
QListIterator
是隻讀遍歷器,不能插入或者刪除資料。如果需要這些操作,我們可以使用QMutableListIterator
。來看下面的程式碼:
這段程式碼使用QMutableListIterator
遍歷集合,如果其值是奇數則將其刪除。在每次迴圈中都要呼叫next()
函式。正如前面所說,它會跳過其後的一個元素。remove()
函式會刪除我們剛剛跳過的元素。呼叫remove()
函式並不會將遍歷器置位不可用,因此我們可以連續呼叫這個函式。向前遍歷也是類似的,這裡不再贅述。
如果我們需要修改已經存在的元素,使用setValue()
函式。例如:
如同remove()
函式,setValue()
也是對剛剛跳過的元素進行操作。實際上,next()
函式返回的是集合元素的非 const 引用,因此我們根本不需要呼叫setValue()
函式:
QMapItrator
也是類似的。例如,使用QMapItrator
我們可以將資料從QMap
複製到QHash
:
STL 風格的遍歷器
STL 風格的遍歷器從 Qt 2.0 就開始提供。這種遍歷器能夠相容 Qt 和 STL 的通用演算法,並且為速度進行了優化。同 Java 風格遍歷器類似,Qt 也提供了兩種 STL 風格的遍歷器:一種是隻讀訪問,一種是讀寫訪問。我們推薦儘可能使用只讀訪問,因為它們要比讀寫訪問的遍歷器快一些。
容器 | 只讀遍歷器 | 讀寫遍歷器 |
QList<T> ,QQueue<T> | QList<T>::const_iterator | QList<T>::iterator |
QLinkedList<T> | QLinkedList<T>::const_iterator | QLinkedList<T>::iterator |
QVector<T> ,QStack<T> | QVector<T>::const_iterator | QVector<T>::iterator |
QSet<T> | QSet<T>::const_iterator | QSet<T>::iterator |
QMap<Key, T> ,QMultiMap<Key, T> | QMap<Key, T>::const_iterator | QMap<Key, T>::iterator |
QHash<Key, T> ,QMultiHash<Key, T> | QHash<Key, T>::const_iterator | QHash<Key, T>::iterator |
STL 風格的遍歷器具有類似陣列指標的行為。例如,我們可以使用 ++ 運算子讓遍歷器移動到下一個元素,使用 * 運算子獲取遍歷器所指的元素。對於QVector
和QStack
,雖然它們是在連續記憶體區儲存元素,遍歷器型別是typedef T *
,const_iterator
型別則是typedef const T *
。
我們還是以QList
和QMap
為例,理由如上。下面是有關QList
的相關程式碼:
不同於 Java 風格遍歷器,STL 風格遍歷器直接指向元素本身。容器的begin()
函式返回指向該容器第一個元素的遍歷器;end()
函式返回指向該容器最後一個元素之後的元素的遍歷器。end()
實際是一個非法位置,永遠不可達。這是為跳出迴圈做的一個虛元素。如果集合是空的,begin()
等於end()
,我們就不能執行迴圈。
下圖是 STL 風格遍歷器的示意圖:
我們使用const_iterator
進行只讀訪問,例如:
QMap
和QHash
的遍歷器,* 運算子返回集合鍵值對。下面的程式碼,我們列印出QMap
的所有元素:
由於有隱式資料共享(我們會在後面的章節介紹該部分內容),即使一個函式返回集合中元素的值也不會有很大的代價。Qt API 包含了很多以值的形式返回QList
或QStringList
的函式(例如QSplitter::sizes()
)。如果你希望使用
STL 風格的遍歷器遍歷這樣的元素,應該使用遍歷器遍歷容器的拷貝,例如:
對於那些返回集合的 const 或非 const 引用的函式,就不存在這個問題。
另外,隱式資料共享對 STL 風格遍歷器造成的另一個影響是,當一個容器正在被一個遍歷器遍歷的時候,不能對這個容器進行拷貝。如果你必須對其進行拷貝,那麼就得萬分小心。例如,
雖然這個例子只演示了QVector
,但實際上,這個問題適用於所有隱式資料共享的容器類。
foreach
關鍵字
如果我們僅僅想要遍歷集合所有元素,我們可以使用 Qt 的foreach
關鍵字。這個關鍵字是 Qt 特有的,通過前處理器進行處理。C++ 11 也提供了自己的foreach
關鍵字,不過與此還是有區別的。
foreach
的語法是foreach
(variable, container)
。例如,我們使用foreach
對QLinkedList
進行遍歷:
這段程式碼與下面是等價的:
如果型別名中帶有逗號,比如QPair<int, int>
,我們只能像上面一樣,先建立一個物件,然後使用foreach
關鍵字。如果沒有逗號,則可以直接在foreach
關鍵字中使用新的物件,例如:
Qt 會在foreach
迴圈時自動拷貝容器。這意味著,如果在遍歷時修改集合,對於正在進行的遍歷是沒有影響的。即使不修改容器,拷貝也是會發生的。但是由於存在隱式資料共享,這種拷貝還是非常迅速的。
因為foreach
建立了集合的拷貝,使用集合的非 const 引用也不能實際修改原始集合,所修改的只是這個拷貝。
相關文章
- vector容器1(新增元素,遍歷元素)
- Qt foreach關鍵字遍歷容器QT
- Python字典的遍歷,包括key遍歷/value遍歷/item遍歷/Python
- js的map遍歷和array遍歷JS
- 二叉樹建立,前序遍歷,中序遍歷,後序遍歷 思路二叉樹
- 二叉樹的建立、前序遍歷、中序遍歷、後序遍歷二叉樹
- jQuery 遍歷jQuery
- 遍歷 FlowDocument
- Javascript樹(一):廣度遍歷和深度遍歷JavaScript
- jQuery遍歷函式,javascript中的each遍歷jQuery函式JavaScript
- jQuery的遍歷結構設計之遍歷同胞jQuery
- jQuery的遍歷結構設計之遍歷祖先jQuery
- 圖的遍歷演算法-馬遍歷棋盤演算法
- 陣列遍歷陣列
- 資料遍歷
- jQuery 遍歷方法jQuery
- jQuery 遍歷 – 祖先jQuery
- Java遍歷PropertiesJava
- 二叉樹的廣度遍歷和深度遍歷()二叉樹
- 非遞迴實現先序遍歷和中序遍歷遞迴
- JS中的遍歷JS
- indexedDB 遍歷資料Index
- DOM元素的遍歷
- JavaScript Iterator遍歷器JavaScript
- jquery遍歷節點jQuery
- Jquery之遍歷元素jQuery
- 陣列遍歷方法陣列
- vue遍歷map物件Vue物件
- jQuery 遍歷 – 過濾jQuery
- jQuery 遍歷 – 後代jQuery
- JNI 檔案遍歷
- thymeleaf模板 遍歷物件物件
- JavaScript 中的遍歷JavaScript
- Linuxshell遍歷Linux
- javascript如何遍歷表格JavaScript
- jQuery遍歷-slice()方法jQuery
- java Map Set遍歷Java
- Qt遍歷子物件QT物件