在C++里做大型程序時,少不了要與數組、鏈表等數據結構打交道。就是最簡單的字符串也常常讓頭痛萬分,Qt中有QString解決了字符串的頭痛,那么其他數組等有沒有更簡單的解決方案呢?Qt作為一款優秀的類型庫,當然不會沒考慮這些。Qt提供了大量的“容器類”,專門用於以某種方式存儲大量內容,QString其實只是這大量的容器類的一種。
我在這里介紹:
QVector(數組)、QLinkedList(鏈表)、QMap(映射表)、QHash(哈希表)
QVector,是Qt對所有數組的封裝,比如我們想要一個int類型數組,我們原先會寫int array[10],我們在Qt里可以寫QVector<int> array(10)
賦值的時候,我們依然可以照舊array[5]=4;想獲取某一項的值也還可以array[9],也就是說,原來的特性我們還可以用。
那么QVector有什么好處呢?
·我們可以用count函數獲知數組中有多少個元素,方便遍歷
·原先我們必須預定義好大小,而用QVector我們雖然最好也先定義好大小,但是預先不定義也可以。
我們可以使用append函數或者<<操作符來在數組最后端添加元素而不用擔心溢出問題。
QVector<double> vect(2); vect[0] = 1.0; vect[1] = 2.0; for (int i = 0; i < vect.count(); ++i) { cout << vect[i] << endl; } for (int i = 0; i < vect.count(); ++i) { cout << vect.at(i) << endl; }
要使用索引方式設定元素,必須先配置好夠長的空間,否則會發生超出索引範圍的錯誤,使用[]運算子指定索引存取的方式是比較方便,但在某些場合下,使用at()方法會較有效率一些,這涉及Qt的隱式共享機制,稍後再作介紹。
您也可以使用QVector的append()方法來加入元素,使用remove()方法來移除元素,使用insert()方法來插入元素,例如append()的使用如下:
vect.append(3.0);vect.append(4.0);
或者是使用<<運算子附加元素:
vect << 5.0 << 6.0;
QVector 也重載了一些其它的運算子,以及提供了一些其它可用的方法,請查詢Qt線上文件有關於QVector的介紹。QVector提供的是鄰接的 記憶體空間以存取物件,所以對於循序存取或使
QList 的子類別QStringList為Qt中應用很廣的類別,可以讓您儲存QString物件,QList的子類別QQueue則提供了佇列結構的容器管理。
以上先列出QVector、QLinkedList及QList的使用比較:
-
如果想要有連續鄰接的記憶體空間來存放元件,則使用QVector 。
-
如果需要真正的鏈結資料結構,並使用基於迭代器的存取方式,則使用QLinkedList 。
-
在大部份情況下,QList 可以滿足快速存取、插入、移除的需求,並可提供基於索引的存取方式。
QList<QString> list; list << "caterpillar" << "momor" << "bush"; QListIterator<QString> iterator(list); while (iterator.hasNext()) { cout << iterator.next().toAscii().data() << endl; }
與Java 迭代器類似的,hasNext()測試是否有下一個元素,next()傳回下一個元素,其它還有hasPrevious()、previous()等方法 可以使用。Java風格的迭代器有兩種:唯讀與可讀寫。QListIterator是唯讀迭代器,對於可讀寫迭代器,命名上會加上Mutable,例 如QMutableListIterator,除了next()、previous()等方法之外,還提供了insert()、remove()等方法可 以使用,例如:
QLinkedList<QString> list; list << "caterpillar" << "momor" << "bush"; QMutableLinkedListIterator<QString> rwIterator(list); while (rwIterator.hasNext()) { if(rwIterator.next() == "momor") { rwIterator.insert("bee"); break; } } QLinkedListIterator<QString> rIterator(list); while ( rIterator.hasNext() ) { cout << rIterator.next().toAscii().data() << endl; }
QMap是個有趣的東西,想在裸露的底層C++實現它頗為麻煩。數組建立的是從0開始的連續數字與數據的對應關系,而QMap的作用就是,讓任意一種數據類型對應於另一種數據類型。聲明時如此:QMap<索引類型,數據類型> 變量名。他的表現有點類似於PHP編程的array
比如:
#include<QMap> ... void someFunction() { QMap<QString,QString> map; map["Hello"]="World"; QMap<int,double> i2d; i2d[5231]=32.4213; //遍歷比較特殊,得這樣: QMapIterator<QString,QString> i(map); while(i.hasNext()) doSomething(i.next()); }
如果我們想用[]操作符訪問某一項,但那一項並不存在,那就會自動創建,如果不想創建空白項可以使用value函數,如i2d.value(123,-0.1);這里如果i2d[123]存在的話就返回那一項,否則返回默認值-0.1,這個默認值可以不寫,那樣Qt系統就會使用Qt默認的默認值……。可以用take函數(讓人糾結的函數名)來刪除某一項。
QHash哈希表,與QMap幾乎一樣,但是它更高效,不過使用QHash要求作為索引的類型可以用==比較並且有對應的函數qHash,Qt里面自帶了一部分,比如QString、各類整數、指針、QByteArray、QChar等都可以直接作為QHash的索引。因為QHash更高效,所以建議盡量使用QHash。
QMap與QHash都是一對一或多對以的映射,可以使用QMultiMap與QMultiHash建立一對多的映射。
比如QMultiMap <int,QString> map;
map[3]="Hello";
map.insert(3,"World");
調用map[3]時,就會得到一個QList<QString>類型的變量。
遍歷時依然可用 QMapIterator
本文介紹的是Qt QHash 和QMap的區別,關於容器類可以查閱更多的資料,首先我們先把QHash 和QMap區分開來。內容如下。
QMap提供了一個從類項為key的鍵到類項為T的直的映射,通常所存儲的數據類型是一個鍵對應一個直,並且按照Key的次序存儲數據,這個類也支持一鍵多值的情況,用類QMultiMap
QHash具有和QMap幾乎完全一樣的APi,此類維護這一張哈希表,表的大小和數據項是自適應的,QHash是以任意的順序住址他的數據,,當然了他也是可以支持一鍵多值的,QMultiHash
兩種之間的區別是:
QHash查找速度上顯著於QMap
QHash以任意的方式進行存儲,而QMap則是以key順序進行存儲.
Qhash 的鍵類型必須提供operator==()和yige 全局的qHash(key)函數。而QMap的鍵類型key必須提供operator<()函數.
他們同樣也是有兩種風格的迭代容器。用來進行遍歷的。
STL 風格的
QMap<key,T> QMap<key,T>::const_iterator QMap<key,T>::iterator//同樣中間那個也是只讀的,最后那個是讀寫的。下面以一個例子來進行說明:
1.#include <QDebug> 2.int main(int argc, char *argv[]) 3.{ 4. QMap<QString, QString> map; 5. map.insert("beijing", "111"); 6. map.insert("shanghai", "021"); 7. map.insert("tianjin", "022"); 8. map.insert("chongqing", "023"); 9. map.insert("jinan", "0531"); 10. map.insert("wuhan", "027"); 11.QMap<QString, QString>::const_iterator i; 12. for( i=map.constBegin(); i!=map.constEnd(); ++i) 13. qDebug() <<i.key() <<" " <<i.value(); 14. QMap<QString, QString>::iterator mi; 15.mi = map.find("beijing"); 16. if(mi != map.end()) 17. mi.value() = "010"; 18. QMap<QString, QString>::const_iterator modi; 19. qDebug() << ""; 20. for( modi=map.constBegin(); modi!=map.constEnd(); ++modi) 21. qDebug() <<modi.key() <<" " <<modi.value(); 22. return 0; 23.}
小結:關於Qt 中QHash 和QMap的區別,相信你看完之后,應該很了然一新了。如果你需要對內存分配做優化,Qt的容器提供了三大內存分配函數,reserve(size),顯示預分配size的內存; capacity(),返回已分配內存;squeeze()釋放所有的未占用的內存;當你知道QHash的大小時,可以使用reserve函數預先分配內存。
今天我們來說說Qt容器類中的關聯存儲容器。所謂關聯存儲容器,就是容器中存儲的一般是二元組,而不是單個的對象。二元組一般表述為,也就是“鍵-值對”。
首先,我們看看數組的概念。數組可以看成是一種形式的鍵-值對,它的Key只能是int,而值的類型是Object,也就是任意類型(注意,這里我們只是說數組可以是任意類型,這個Object並不必須是一個對象)。現在我們擴展數組的概念,把Key也做成任意類型的,而不僅僅是int,這樣就是一個關聯容器了。如果學過數據結構,典型的關聯容器就是散列(Hash Map,哈希表)。Qt提供兩種關聯容器類型:QMap和QHash。
QMap是一種鍵-值對的數據結構,它實際上使用跳表skip-list實現,按照K進行升序的方式進行存儲。使用QMap的insert()函數可以向QMap中插入數據,典型的代碼如下:
QMap map; map.insert("eins", 1); map.insert("sieben", 7); map.insert("dreiundzwanzig", 23); |
同樣,QMap也重載了[]運算符,你可以按照數組的復制方式進行使用:
map["eins"] = 1; map["sieben"] = 7; map["dreiundzwanzig"] = 23; |
[]操作符同樣也可以像數組一樣取值。但是請注意,如果在一個非const的map中,使用[]操作符取一個不存在的Key的值,則這個Key會被自動創建,並將其關聯的value賦予一個空值。如果要避免這種情況,請使用QMap的value()函數:
int val = map.value("dreiundzwanzig"); |
如果key不存在,基本類型和指針會返回0,對象類型則會調用默認構造函數,返回一個對象,與[]操作符不同的是,value()函數不會創建一個新的鍵-值對。如果你希望讓不存在的鍵返回一個默認值,可以傳給value()函數第二個參數:
int seconds = map.value("delay", 30); |
這行代碼等價於:
int seconds = 30; if (map.contains("delay")) seconds = map.value("delay"); |
QMap中的K和T可以是基本數據類型,如int,double,可以是指針,或者是擁有默認構造函數、拷貝構造函數和賦值運算符的類。並且K必須要重載<運算符,因為QMap需要按K升序進行排序。
QMap提供了keys()和values()函數,可以獲得鍵的集合和值的集合。這兩個集合都是使用QList作為返回值的。
Map是單值類型的,也就是說,如果一個新的值分配給一個已存在的鍵,則舊值會被覆蓋。如果你需要讓一個key可以索引多個值,可以使用QMultiMap。這個類允許一個key索引多個value,如:
QMultiMap multiMap; multiMap.insert(1, "one"); multiMap.insert(1, "eins"); multiMap.insert(1, "uno"); QList vals = multiMap.values(1); |
QHash是使用散列存儲的鍵-值對。它的接口同QMap幾乎一樣,但是它們兩個的實現需求不同。QHash的查找速度比QMap快很多,並且它的存儲是不排序的。對於QHash而言,K的類型必須重載了==操作符,並且必須被全局函數qHash()所支持,這個函數用於返回key的散列值。Qt已經為int、指針、QChar、QString和QByteArray實現了qHash()函數。
QHash會自動地為散列分配一個初始大小,並且在插入數據或者刪除數據的時候改變散列的大小。我們可以使用reserve()函數擴大散列,使用squeeze()函數將散列縮小到最小大小(這個最小大小實際上是能夠存儲這些數據的最小空間)。在使用時,我們可以使用reserve()函數將數據項擴大到我們所期望的最大值,然后插入數據,完成之后使用squeeze()函數收縮空間。
QHash同樣也是單值類型的,但是你可以使用insertMulti()函數,或者是使用QMultiHash類來為一個鍵插入多個值。另外,除了QHash,Qt也提供了QCache來提供緩存,QSet用於僅存儲key的情況。這兩個類同QHash一樣具有K的類型限制。
遍歷關聯存儲容器的最簡單的辦法是使用Java風格的遍歷器。因為Java風格的遍歷器的next()和previous()函數可以返回一個鍵-值對,而不僅僅是值,例如:
QMap map; ... int sum = 0; QMapIterator i(map); while (i.hasNext()) sum += i.next().value(); |
如果我們並不需要訪問鍵-值對,可以直接忽略next()和previous()函數的返回值,而是調用key()和value()函數即可,如:
QMapIterator i(map); while (i.hasNext()) { i.next(); if (i.value() > largestValue) { largestKey = i.key(); largestValue = i.value(); } } |
Mutable遍歷器則可以修改key對應的值:
QMutableMapIterator i(map); while (i.hasNext()) { i.next(); if (i.value() < 0.0) i.setValue(-i.value()); } |
如果是STL風格的遍歷器,則可以使用它的key()和value()函數。而對於foreach循環,我們就需要分別對key和value進行循環了:
QMultiMap map; ... foreach (QString key, map.keys()) { foreach (int value, map.values(key)) { doSomething(key, value); } } |