概述
Qt
庫提供了一組通用的基于模板的容器類。這些類可用于存儲指定類型的項。例如,如果需要一個可調整大小的QString
數組,可以使用QVector<QString>
。
這些容器類被設計成比STL
容器更輕、更安全、更易于使用。如果不熟悉STL
,或者更喜歡用Qt方式
進行變成,那么就可以使用這些類來代替STL類
。
容器類是隱式共享的,它們是可重入的,并且它們針對速度、低內存消耗和最小的內聯代碼擴展進行了優化,從而產生更小的可執行文件。此外,當所有訪問它們的線程都將它們用作只讀容器時,它們是線程安全的。
要遍歷存儲在容器中的項,可以使用兩種類型的迭代器之一:java風格的迭代器
和stl風格的迭代器
。java風格的迭代器
更容易使用并提供高級功能,而STL風格的迭代器
稍微更有效,可以與Qt
和STL
的泛型算法一起使用。
Qt
還提供了foreach
關鍵字,它可以很容易地遍歷容器中存儲的所有項。
Qt容器類介紹
Qt
提供了以下順序容器:QList,
QLinkedList
, QVector
, QStack
和QQueue
。對于大多數應用程序,QList
是最好的類型。雖然它是作為數組列表實現的,但它提供了非常快的前置和追加。如果需要一個鏈表,使用QLinkedList
;如果想讓項目占用連續的內存位置,使用QVector
。QStack
和QQueue
是提供后進先出
和先進先出
語義的方便類。
Qt
還提供了這些關聯容器:QMap
、QMultiMap
、QHash
、QMultiHash
和QSet
。“Multi”
容器方便地支持與單個鍵關聯的多個值。“哈希”
容器通過使用哈希函數而不是對排序集進行二進制搜索來提供更快的查找。
作為特殊情況,QCache
和qconsecuousscache
類在有限的緩存存儲中提供了有效的對象哈希查找。
以下是常用容器類:
?
類 | 介紹 |
---|---|
QList | 這是目前為止最常用的容器類。它存儲可以通過索引訪問的給定類型(T)的值列表。在內部,QList使用數組實現,確保基于索引的訪問非常快。 |
QLinkedList | 可以使用QList::append()和QList::prepend()在列表的兩端添加項,也可以使用QList::insert()在中間插入項。與任何其他容器類不同,QList經過了高度優化,可以在可執行文件中擴展到盡可能少的代碼。QStringList繼承自QList。這類似于QList,不同之處在于它使用迭代器而不是整數索引來訪問項。在大列表中插入時,它還提供了比QList更好的性能,并且具有更好的迭代器語義。(只要QLinkedList中的元素存在,指向該元素的迭代器就保持有效,而指向QList的迭代器在插入或刪除后就會失效。) |
QVector | 這將給定類型的值數組存儲在內存中的相鄰位置。在vector的前面或中間插入可能會非常慢,因為它可能導致大量項必須在內存中移動一個位置。 |
QStack | 這是QVector的一個方便子類,提供“后進先出”(LIFO)語義。它為QVector中已經存在的函數添加了以下函數:push()、pop()和top()。 |
QQueue | 這是QList的一個方便子類,提供“先進先出”(FIFO)語義。它向QList中已經存在的函數添加了以下函數:enqueue()、dequeue()和head()。 |
QSet | 這提供了一個具有快速查找功能的單值數學集。 |
QMap<Key, T> | 它提供了一個字典(關聯數組),將Key類型的鍵映射到t類型的值。通常每個鍵都與單個值相關聯。QMap按Key順序存儲數據;如果順序不重要,QHash是一個更快的選擇。 |
QMultiMap<Key, T> | 這是QMap的一個方便的子類,它為多值映射提供了一個很好的接口,即一個鍵可以與多個值相關聯的映射。 |
QHash<Key, T> | 它具有與QMap幾乎相同的API,但提供了明顯更快的查找。QHash以任意順序存儲數據。 |
QMultiHash<Key, T> | 這是QHash的一個方便的子類,它為多值哈希提供了一個很好的接口。 |
?
容器是可以嵌套。例如,完全可以使用QMap<QString, QList<int>>
,其中鍵類型是QString
,值類型是QList<int>
。
容器在單獨的頭文件中定義,其名稱與容器相同(例如,<QLinkedList>
)。為方便起見,在<QtContainerFwd>
中向前聲明了容器。
存儲在各種容器中的值可以是任何可賦值的數據類型。要符合條件,類型必須提供默認構造函數、復制構造函數和賦值操作符。這涵蓋了可能想要存儲在容器中的大多數數據類型,包括基本類型,如int
和double
,指針類型,以及Qt
數據類型,如QString
, QDate
和QTime
,但它不包括QObject
或任何QObject
子類(QWidget
, QDialog
, QTimer
等)。如果嘗試實例化QList<QWidget>
,編譯器將報錯QWidget
的復制構造函數和賦值操作符被禁用。如果希望將這些類型的對象存儲在容器中,請將它們存儲為指針,例如QList<QWidget *>
。
下面是一個滿足可賦值數據類型要求的自定義數據類型示例:
class Employee{public:Employee() {}Employee(const Employee &other);Employee &operator=(const Employee &other);private:QString myName;QDate myDateOfBirth;};
?
如果沒有提供復制構造函數或賦值操作符,c++提供了執行逐個成員復制的默認實現。在上面的例子中,這就足夠了。此外,如果不提供任何構造函數,c++還提供一個默認構造函數,該構造函數使用默認構造函數初始化其成員。盡管它沒有提供任何顯式構造函數或賦值操作符,但下列數據類型可以存儲在容器中:
struct Movie{int id;QString title;QDate releaseDate;};
?
有些容器對它們可以存儲的數據類型有額外的要求。例如,QMap<Key, T>
的Key
類型必須提供操作符<()
。這些特殊需求在類的詳細描述中都有文檔。在某些情況下,特定功能有特殊要求;這些是按功能描述的。如果不滿足要求,編譯器總是會發出一個錯誤。
Qt的容器提供了operator<<()
和operator>>()
,因此它們可以很容易地使用QDataStream
進行讀寫。這意味著存儲在容器中的數據類型還必須支持operator<<()
和operator>>()
。提供這樣的支持是直截了當的;下面是我們如何為上面的Movie結構體做到這一點:
QDataStream &operator<<(QDataStream &out, const Movie &movie){out << (quint32)movie.id << movie.title<< movie.releaseDate;return out;}QDataStream &operator>>(QDataStream &in, Movie &movie){quint32 id;QDate date;in >> id >> movie.title >> date;movie.id = (int)id;movie.releaseDate = date;return in;}
?
某些容器類函數的文檔引用默認構造的值;例如,QVector
用默認構造的值自動初始化它的項,如果指定的鍵不在映射中,QMap::value()
返回一個默認構造的值。對于大多數值類型,這僅僅意味著使用默認構造函數創建值(例如,QString
的空字符串)。但是對于int
和double
這樣的基本類型,以及指針類型,c++
語言沒有指定任何初始化;在這種情況下,Qt
的容器會自動將該值初始化為0
。
示例
以下是使用QList
和QMap
進行介紹,其他幾個容器可以參考這兩個進行操作,因為他們的API
很相似。
QList
QList
是Qt中提供的一個容器類,它是一個動態數組,可以自動調整大小以容納新元素
- append:在列表的末尾添加一個新元素。
QList<int> list;
list.append(1);
list.append(2);
list.append(3);
// list: {1, 2, 3}
- prepend:在列表的開頭添加一個新元素。
QList<int> list;
list.prepend(1);
list.prepend(2);
list.prepend(3);
// list: {3, 2, 1}
- insert:在列表的指定位置插入一個新元素。
QList<int> list;
list << 1 << 2 << 4;
list.insert(2, 3);
// list: {1, 2, 3, 4}
- replace:用新元素替換列表中的一個元素。
QList<int> list;
list << 1 << 2 << 3;
list.replace(1, 4);
// list: {1, 4, 3}
- removeAt:從列表中刪除指定索引處的元素。
QList<int> list;
list << 1 << 2 << 3;
list.removeAt(1);
// list: {1, 3}
- swap:交換列表中兩個元素的位置。
QList<int> list;
list << 1 << 2 << 3;
list.swap(0, 2);
// list: {3, 2, 1}
- contains:檢查列表中是否包含某個元素。
QList<int> list;
list << 1 << 2 << 3;
if (list.contains(2)) {qDebug() << "2 is in the list.";
}
- count:返回列表中某個元素的數量。
QList<int> list;
list << 1 << 2 << 2 << 3 << 2;
int count = list.count(2); // count is 3
- indexOf:返回列表中某個元素的第一個索引。
QList<int> list;
list << 1 << 2 << 2 << 3 << 2;
int index = list.indexOf(2); // index is 1
- lastIndexOf:返回列表中某個元素的最后一個索引。
QList<int> list;
list << 1 << 2 << 2 << 3 << 2;
int index = list.lastIndexOf(2); // index is 4
- empty:檢查列表是否為空。
QList<int> list;
if (list.empty()) {qDebug() << "The list is empty.";
}
- size:返回列表的大小。
QList<int> list;
list << 1 << 2 << 3;
int size = list.size(); // size is 3
- clear:刪除列表中的所有元素。
QList<int> list;
list << 1 << 2 << 3;
list.clear();
// list is now empty
對于只讀的訪問,一般使用at()
函數,因為比"[ ]"
操作符快很多。
QMap
QMap
是Qt中提供的一個容器類,它是一個關聯數組,可以將鍵值對存儲在一起.
- insert:在映射中插入一個鍵值對。
QMap<QString, int> map;
map.insert("Bob", 25);
map.insert("Alice", 30);
map.insert("Charlie", 35);
// map: { "Alice": 30, "Bob": 25, "Charlie": 35 }
- replace:用新值替換映射中的一個值。
QMap<QString, int> map;
map.insert("Bob", 25);
map.insert("Alice", 30);
map.insert("Charlie", 35);
map.replace("Bob", 20);
// map: { "Alice": 30, "Bob": 20, "Charlie": 35 }
- remove:從映射中刪除指定鍵的值。
QMap<QString, int> map;
map.insert("Bob", 25);
map.insert("Alice", 30);
map.insert("Charlie", 35);
map.remove("Bob");
// map: { "Alice": 30, "Charlie": 35 }
- swap:交換兩個映射的內容。
QMap<QString, int> map1, map2;
map1.insert("Bob", 25);
map1.insert("Alice", 30);
map2.insert("Charlie", 35);
map2.insert("Dave", 40);
map1.swap(map2);
// map1: { "Charlie": 35, "Dave": 40 }
// map2: { "Alice": 30, "Bob": 25 }
- contains:檢查映射中是否包含指定鍵。
QMap<QString, int> map;
map.insert("Bob", 25);
map.insert("Alice", 30);
if (map.contains("Bob")) {qDebug() << "Bob is in the map.";
}
- count:返回映射中指定鍵的數量。
QMap<QString, int> map;
map.insert("Bob", 25);
map.insert("Alice", 30);
map.insert("Charlie", 35);
int count = map.count("Bob"); // count is 1
- value:返回映射中指定鍵的值,如果鍵不存在則返回默認值。
QMap<QString, int> map;
map.insert("Bob", 25);
map.insert("Alice", 30);
int value = map.value("Charlie", 0); // value is 0
- keys:返回映射中所有的鍵。
QMap<QString, int> map;
map.insert("Bob", 25);
map.insert("Alice", 30);
QList<QString> keys = map.keys(); // keys: { "Bob", "Alice" }
- values:返回映射中所有的值。
QMap<QString, int> map;
map.insert("Bob", 25);
map.insert("Alice", 30);
QList<int> values = map.values(); // values: { 25, 30 }
- empty:檢查映射是否為空。
QMap<QString, int> map;
if (map.empty()) {qDebug() << "The map is empty.";
}
- size:返回映射中鍵值對的數量。
QMap<QString, int> map;
map.insert("Bob", 25);
map.insert("Alice", 30);
int size = map.size(); // size is 2
- clear:刪除映射中的所有鍵值對。
QMap<QString, int> map;
map.insert("Bob", 25);
map.insert("Alice", 30);
map.clear();
// map is now empty
遍歷容器
要遍歷存儲在容器中的項,可以使用兩種類型的迭代器之一:java風格的迭代器
和stl風格的迭代器
。java風格的迭代器
更容易使用并提供高級功能,而STL風格的迭代器
稍微更有效,可以與Qt
和STL
的泛型算法一起使用。
Qt
還提供了foreach
關鍵字,它可以很容易地遍歷容器中存儲的所有項。
在本文檔中,將集中討論QList和QMap。QLinkedList、QVector和QSet的迭代器類型與QList的迭代器具有完全相同的接口;類似地,QHash的迭代器類型與QMap的迭代器具有相同的接口。
與stl風格的迭代器(將在下面介紹)不同,java風格的迭代器指向項目之間,而不是直接指向項目。由于這個原因,它們要么指向容器的最開始(在第一個項目之前),要么指向容器的最末尾(在最后一個項目之后),要么指向兩個項目之間。下圖用紅色箭頭顯示了包含四個元素的列表的有效迭代器位置:
下面,按順序遍歷QList中的所有元素,并將它們打印到控制臺:
QList<QString> list;list << "A" << "B" << "C" << "D";QListIterator<QString> i(list);while (i.hasNext())qDebug() << i.next();
它的工作原理如下:將要迭代的QList傳遞給QListIterator
構造函數。此時,迭代器位于列表中第一項的前面(在項“A”之前)。然后調用hasNext()
來檢查迭代器后是否有項。如果有,則調用next()跳過該項。next()函數返回它跳過的項。對于QList,該項的類型為QString。
下面是如何在QList中向后迭代:
QListIterator<QString> i(list);i.toBack();while (i.hasPrevious())qDebug() << i.previous();
代碼與向前迭代是對稱的,除了我們首先調用toBack()將迭代器移動到列表中的最后一項之后。
下圖說明了在迭代器上調用next()和previous()的效果:
QListIterator
常用API:
方法 | 行為 |
---|---|
toFront() | 將迭代器移動到列表的前面(在第一項之前) |
toBack() | 將迭代器移動到列表的后面(在最后一項之后) |
hasNext() | 如果迭代器不在列表的末尾,則返回true |
next() | 返回下一項并將迭代器向前移動一個位置 |
peekNext() | 不移動迭代器返回下一項 |
hasPrevious() | 如果迭代器不在列表的前面,則返回true |
previous() | 返回前一項并將迭代器向后移動一個位置 |
peekPrevious() | 不移動迭代器返回前一項 |
QListIterator
不提供在迭代時從列表中插入或刪除項的函數。要做到這一點,必須使用QMutableListIterator
。
下面是一個使用QMutableListIterator
從QList<int>
中刪除所有奇數的例子:
QMutableListIterator<int> i(list);while (i.hasNext()) {if (i.next() % 2 != 0)i.remove();}
循環中的next()調用每次都進行。它跳過列表中的下一項。remove()函數刪除了我們從列表中跳過的最后一項。調用remove()不會使迭代器失效,所以繼續使用它是安全的。這在向后迭代時同樣有效:
QMutableListIterator<int> i(list);i.toBack();while (i.hasPrevious()) {if (i.previous() % 2 != 0)i.remove();}
就像remove()一樣,setValue()對我們跳過的最后一項進行操作。如果向前迭代,這是迭代器前面的項;如果向后迭代,這是迭代器后面的項。
next()函數返回對列表中項目的非const引用。對于簡單的操作,我們甚至不需要setValue():
QMutableListIterator<int> i(list);while (i.hasNext())i.next() *= 2;
如上所述,QLinkedList、QVector和QSet的迭代器類具有與QList完全相同的API。現在我們將轉向QMapIterator,它有點不同,因為它對(鍵,值)對進行迭代。
與QListIterator一樣,QMapIterator提供toFront()、toBack()、hasNext()、next()、peekNext()、hasPrevious()、previous()和peekPrevious()。鍵和值組件是通過對next()、peekNext()、previous()或peekPrevious()返回的對象調用key()和value()來提取的。
下面的例子刪除所有以"City"結尾的(capital, country)對:
QMap<QString, QString> map;map.insert("Paris", "France");map.insert("Guatemala City", "Guatemala");map.insert("Mexico City", "Mexico");map.insert("Moscow", "Russia");...QMutableMapIterator<QString, QString> i(map);while (i.hasNext()) {if (i.next().key().endsWith("City"))i.remove();}
QMapIterator還提供了一個key()和一個value()函數,它們直接對迭代器進行操作,并返回迭代器跳到上面的最后一項的鍵和值。例如,下面的代碼將QMap的內容復制到QHash中:
QMap<int, QWidget *> map;QHash<int, QWidget *> hash;QMapIterator<int, QWidget *> i(map);while (i.hasNext()) {i.next();hash.insert(i.key(), i.value());}
如果要遍歷具有相同值的所有項,可以使用findNext()或findPrevious()。下面是一個例子,刪除所有具有特定值的項:
QMutableMapIterator<int, QWidget *> i(map);while (i.findNext(widget))i.remove();
結論
想改變物質生活,后來發現,改變心態更容易
。