QT不僅支持C++的STL模板庫,同時自己也定義了一套容器類和與之操作的算法類,使用QT定義的這一套庫,可以使在各個平臺的表現相同。QT的這些容器被設計爲更輕便,更安全和更容易使用。容器類是隱含共享(implicitly)的,可重入(reentrant)的和線程安全的。爲容器遍歷提供了兩種迭代器(java-style,STL-style),並且QT提供了foreach關鍵字,更加方便元素遍歷。
連續容器:
QVector<T>
它是QT裏最常見的容器類,它對應STL中的Vector<T>容器,是動態數組,提供快速的索引存取。
QList<T> QLinkList<T> QVarLengthArray<T>提供類似的功能。
可以參考幫助文檔,查看QList的具體使用方法,以下列出部分使用示例:
1.創建各種類型的vector:
QVector<int> integerVector;
QVector<QString> stringVector;
2.創建一定數目項的vector
QVector<QString> vector(200);
3.帶初始化的創建vector
QVector<QString> vector(200, "Pass");
也可以使用fill賦值:
QVector<QString> vector(3);
vector.fill("Yes");
// vector: ["Yes", "Yes", "Yes"]
vector.fill("oh", 5);
// vector: ["oh", "oh", "oh", "oh", "oh"]
4.QVector像c++的Vector一樣提供[]下標操作,並從0項開始。還提供at()的只讀操作,at()比[]更快,因爲它不會導致深度複製。
if (vector[0] == "Liz")
vector[0] = "Elizabeth";
for (int i = 0; i < vector.size(); ++i) {
if (vector.at(i) == "Alfonso")
cout << "Found Alfonso at position " << i << endl;
}
5.你可以使用indexOf,lastIndexOf來查詢獲取某項值的索引:
QVector<QString> vector;
vector << "A" << "B" << "C" << "B" << "A";
vector.indexOf("B"); // returns 1
vector.indexOf("B", 1); // returns 1
vector.indexOf("B", 2); // returns 3
vector.indexOf("X"); // returns -1
vector.lastIndexOf("B"); // returns 3
vector.lastIndexOf("B", 3); // returns 3
vector.lastIndexOf("B", 2); // returns 1
vector.lastIndexOf("X"); // returns -1
也可以用contains()查看是否包含某元素,返回bool值。
6.通過append,operator<<,prepend,insert添加元素。(對於較大的vector來說,在開頭和中間插入項都是相當耗時的。這種情況更適合使用QLinkedList<T>)
QVector<QString> vector(0);
vector.append("one");
vector.append("two");
vector.append("three");
// vector: ["one", "two", "three"]
QVector<QString> vector(0);
vector << “one” << “two” << “three”;
// vector: ["one", "two", "three"]
QVector<QString> vector;
vector.prepend("one");
vector.prepend("two");
vector.prepend("three");
// vector: ["three", "two", "one"]
QVector<QString> vector;
vector << "alpha" << "beta" << "delta";
vector.insert(2, "gamma");
// vector: ["alpha", "beta", "gamma", "delta"]
7.size() resize() isEmpty() capacity()等和容器大小相關操作。
8.相關轉化:toList() toStdVector()
QVector<double> vect;
vect << "red" << "green" << "blue" << "black";
QList<double> list = vect.toList();
// list: ["red", "green", "blue", "black"]
QVector<double> vector;
vector << 1.2 << 0.5 << 3.14;
std::vector<double> stdvector = vector.toStdVector();
(以下容器操作函數的使用將不再累述,和查閱幫助文檔,並且和QVector的使用方法是一樣的。)
QLinkedList<T>
前面提到,它適合隨機插入項,其原因是它的鏈式結構。他提供了常量時間的插入刪除,卻不能提供快速的隨機存取操作。不提供[]操作,它的遍歷元素是通過迭代器完成的。
QList<T>
它是個數組列表,結合了上面兩種結構的優點,它支持隨機存取,在它的任意一端插入和刪除都是非常快速的並且對於千項以上的列表,在中間插入和刪除也是很快的。學過數據結構的都會清楚這三者的結構區別。如果非要每個項元素都相鄰那就只能用QVector。
QString<t>
它是QList<QString>的子類,它爲字符串操作提供了更通用的操作。
QStack<T> QQueue<T>
他們是棧和隊列結構的實現,QStack提供pop() push() swap() top()操作,它繼承自QVector<T>
QQueue<T>提供dequeue() enqueue() head() swap操作。繼承自QList<T>。
關聯容器
QSet<T>
它提供一個鍵值對集合,可以快速的進行查找,
QMap<Key, T> QMultiMap<Key, T>
QMap是一個以升序鍵順序存儲鍵值對的數據結構,QMultiMap是QMap基礎上提供可以存儲多值的maps,這樣就是說一個鍵對應多個值了。
下面是創建一個QString-int的maps
QMap<QString, int> map;
可以這樣插入值
map["one"] = 1;
map["three"] = 3;
map["seven"] = 7;
也可以這樣:
map.insert("twelve", 12);
查詢一個值使用[] 或者value(“**”)
int num1 = map["thirteen"];
int num2 = map.value("thirteen");
查詢是否存在一個值:
if (map.contains("TIMEOUT"))
timeout = map.value("TIMEOUT");
一般推薦使用contains() value()而不是[]。
QHash<Key, T> QMultiHash<Key, T>
QHash<Key, T>是個在哈希表中存儲鍵值對的結構。它的接口幾乎和QMap相同,但它提供了更快的查找功能。
QHash爲它的內部哈希表自動分配最初的存儲區域,並在有項被插入或者刪除時重新劃分所分配的區域大小。也可以調用reserve()或者squeeze()來指定或者壓縮希望存儲到哈希表的項的數目,以進行性能調整。通常的做法是利用我們預期的最大的項的數目來調用reserve(),然後插入數據,最後如果有多出的項,則調用squeeze()以使內存減到最小。
迭代器
對於每種容器都有兩種風格的迭代器——java風格和STL風格。Java風格的更易於使用而以很少量性能作爲了代價,而STL風格的可以結合STL的算法從而更加強大。
這裏我們主講QList和QMap的迭代器爲例。
Java-Style:
Java風格的迭代器分爲兩種:只讀迭代器,讀寫迭代器。只讀迭代器就是Q*Iterator<T> (例如QVectorIterator<T>),而讀寫迭代器則像QMutable*Iterator<T>這種(例如:QMutableVectorIterator<T>)。
Containers |
Read-only iterator |
Read-write iterator |
QLinkedList<T> |
||
QSet<T> |
QSetIterator<T> |
|
QMapIterator<Key, T> |
QMutableMapIterator<Key, T> |
|
QHash<Key, T>, QMultiHash<Key, T> |
QHashIterator<Key, T> |
QMutableHashIterator<Key, T> |
Java風格迭代器的有效位置:
下面是一個典型的使用例子:
QList<QString> list;
list << "A" << "B" << "C" << "D";
QListIterator<QString> i(list);
while (i.hasNext())
qDebug() << i.next();
下面展示如何向後遍歷
QListIterator<QString> i(list);
i.toBack();
while (i.hasPrevious())
qDebug() << i.previous();
如果左邊有項那麼hasPrevious()將返回true。previous()返回迭代器左邊的項並且往前移一個位置。可以看如圖:
下表是QListIterator 的API及說明
Function |
Behavior |
迭代器移到最前,第一項的前 |
|
迭代器移到最後,最後一項的後面 |
|
如果不是list的最後,就返回true |
|
返回下一項,並迭代器向後移一位 |
|
返回下一項,迭代器並不移動 |
|
如果不是list的最前,就返回true |
|
返回前一項,並迭代器向後移一位 |
|
返回前一項,迭代器並不移動 |
下面是Mutable iterator讀寫迭代器使用說明:
QList<int>移除基數項:
QMutableListIterator<int> i(list);
while (i.hasNext()) {
if (i.next() % 2 != 0)
i.remove();
}
下面是QMap的迭代器示例,用法和前面是類似的:
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();
}
QMap<int, QWidget *> map;
QHash<int, QWidget *> hash;
QMapIterator<int, QWidget *> i(map);
while (i.hasNext()) {
i.next();
hash.insert(i.key(), i.value());
}
STL-Style:
STL風格是迭代器不僅支持Qt的通用算法,還兼容STL的。
和java風格的類似,它也有兩種風格的迭代器,只讀的(const_iterator)和讀寫的(iterator)。
Containers |
Read-only iterator |
Read-write iterator |
QList<T>::const_iterator |
QList<T>::iterator |
|
QLinkedList<T> |
QLinkedList<T>::const_iterator |
QLinkedList<T>::iterator |
QVector<T>::const_iterator |
QVector<T>::iterator |
|
QSet<T> |
QSet<T>::const_iterator |
QSet<T>::iterator |
QMap<Key, T>::const_iterator |
QMap<Key, T>::iterator |
|
QHash<Key, T>, QMultiHash<Key, T> |
QHash<Key, T>::const_iterator |
QHash<Key, T>::iterator |
用過c++ STL庫的就對此很容易上手。下面是QListIterator的例子:
QList<QString> list;
list << "A" << "B" << "C" << "D";
QList<QString>::iterator i;
for (i = list.begin(); i != list.end(); ++i)
*i = (*i).toLower();
STL風格的迭代器可允許的位置與java風格的有所不同
遍歷需要我們自己增加縮減迭代器,例如:
QList<QString>::const_iterator i;
for (i = list.constBegin(); i != list.constEnd(); ++i)
qDebug() << *i;
QList<QString> list;
list << "A" << "B" << "C" << "D";
QList<QString>::iterator i = list.end();
while (i != list.begin()) {
--i;
*i = (*i).toLower();
}
下面是QMap的例子:
QMap<int, int> map;
...
QMap<int, int>::const_iterator i;
for (i = map.constBegin(); i != map.constEnd(); ++i)
qDebug() << i.key() << ":" << i.value();
Foreach關鍵字
下面這個用foreach去遍歷QLinkedList<QString>
QLinkedList<QString> list;
...
QString str;
foreach (str, list)
qDebug() << str;
可以再循環裏使用break
QLinkedList<QString> list;
...
foreach (const QString &str, list) {
if (str.isEmpty())
break;
qDebug() << str;
}
QMap 和 QHash 中,如果你想遍歷鍵和值,你可以用iterators(更快),或者這樣寫:
QMap<QString, int> map;
...
foreach (const QString &str, map.keys())
qDebug() << str << ":" << map.value(str);
或者:
QMultiMap<QString, int> map;
...
foreach (const QString &str, map.uniqueKeys()) {
foreach (int i, map.values(str))
qDebug() << str << ":" << i;
}
類容器類(Container-Like Class)
QVarLengthArray<T>
C++不支持在棧內存中提供可變長度的數組,例如下面:
int myfunc(int n)
{
int table[n + 1]; // WRONG
...
return table[n];
}
只能在堆內存中實現:
int myfunc(int n)
{
int *table = new int[n + 1];
...
int ret = table[n];
delete[] table;
return ret;
}
但是如果myfunc在應用程序內循環中調用非常頻繁,那麼堆內存分配將會變得緩慢,這種情況,QT爲我們提供了QVarLengthArray來解決。
int myfunc(int n)
{
QVarLengthArray<int, 1024> array(n + 1);
...
return array[n];
}
值得注意的是,1.它的API是低水平的(low-level)的,他沒有提供迭代器,和QVector的功能函數。2.如果值是基本類型,它將不會初始化內存。3.QVector使用隱含共享作爲內存的優化,QVarLengthArray並沒有提供,然而,它因爲減少了經常性消費而顯得性能稍微好些,特別是在緊湊的循環裏。總的來說,它是爲了方便用戶使用在很少部分情況。
QCache<Key, T>
提供一個cache去存儲Key-T鍵值對的對象。例如:
QCache<int, Employee> cache;
插入對象到cache
Employee *employee = new Employee;
employee->setId(37);
employee->setName("Richard Schmit");
...
cache.insert(employee->id(), employee);
QCache的好處是自動獲取的對象的擁有權(ownership)。你可以指定插入對象的花費,totalCost() maxCost()。maxCost()默認是100。
QCache<int, MyDataStructure> cache(5000);
QContiguousCache<T>
QContiguousCache是一個提供連續Cache存儲器的模板類。和QCache不同的是,它要求一個約束——相鄰(Contiguous)。這有利於用戶交互界面最普遍的數據需求。這樣的約束使它比QCache消耗更少的內存和處理器週期。
簡單的使用QContiguousCache的方式是使用append() prepend()
MyRecord record(int row) const
{
Q_ASSERT(row >= 0 && row < count());
while(row > cache.lastIndex())
cache.append(slowFetchRecord(cache.lastIndex()+1));
while(row < cache.firstIndex())
cache.prepend(slowFetchRecord(cache.firstIndex()-1));
return cache.at(row);
}
可以查看文檔中Contiguous Cache Example的例子。
QPair<T1, T2>
這個在STL中也是有的(pair)用來儲存鍵值對。它用得更多的是做爲函數的返回值。
看下面這個例子:存儲一個QString鍵double值的QPair
QPair<QString, double> pair;
使用first second來修改值
pair.first = "pi";
pair.second = 3.14159265358979323846;
算法複雜性比較
Constant time: O(1). 常數時間複雜度
Logarithmic time: O(log n). 對數時間複雜度
Linear time: O(n). 線性時間複雜度
Linear-logarithmic time: O(n log n). 線性對數時間複雜度
Quadratic time: O(n²). 平方時間複雜度
順序容器類操作時間複雜度比較:
Index lookup |
Insertion |
Prepending |
Appending |
|
QLinkedList<T> |
O(n) |
O(1) |
O(1) |
O(1) |
QList<T> |
O(1) |
O(n) |
Amort. O(1) |
Amort. O(1) |
QVector<T> |
O(1) |
O(n) |
O(n) |
Amort. O(1) |
關聯容器時間複雜度比較:
|
Insertion |
|||
Average |
Worst case |
Average |
Worst case |
|
QMap<Key, T> |
O(log n) |
O(log n) |
O(log n) |
O(log n) |
QMultiMap<Key, T> |
O(log n) |
O(log n) |
O(log n) |
O(log n) |
QHash<Key, T> |
Amort. O(1) |
O(n) |
Amort. O(1) |
O(n) |
QSet<Key> |
Amort. O(1) |
O(n) |
Amort. O(1) |
O(n) |