Qt中豐富的容器類---數組QVector、鏈表QLinkedList、映射表QMap、哈希表QHash


在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的隱式共享機制,稍後再作介紹。
您也可以使用QVectorappend()方法來加入元素,使用remove()方法來移除元素,使用insert()方法來插入元素,例如append()的使用如下:

vect.append(3.0);vect.append(4.0);

或者是使用<<運算子附加元素:

vect << 5.0 << 6.0;

QVector 也重載了一些其它的運算子,以及提供了一些其它可用的方法,請查詢Qt線上文件有關於QVector的介紹。QVector提供的是鄰接的 記憶體空間以存取物件,所以對於循序存取或使

QList 的子類別QStringListQt中應用很廣的類別,可以讓您儲存QString物件,QList的子類別QQueue則提供了佇列結構的容器管理。
以上先列出QVectorQLinkedListQList的使用比較:

    • 如果想要有連續鄰接的記憶體空間來存放元件,則使用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.} 

  小結:關於QtQHash 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);
   }   }


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM