天天看點

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函數或者<<操作符來在數組最後端添加元素而不用擔心溢出問題。

<code>QVector&lt;</code><code>double</code><code>&gt; vect(2);</code>

<code>vect[0] = 1.0;</code>

<code>vect[1] = 2.0;</code>

<code>for</code> <code>(</code><code>int</code> <code>i = 0; i &lt; vect.count(); ++i) {</code>

<code>    </code><code>cout &lt;&lt; vect[i] &lt;&lt; endl;</code>

<code>}</code>

<code>    </code><code>cout &lt;&lt; vect.at(i) &lt;&lt; endl;</code>

要使用索引方式設定元素,必須先配置好夠長的空間,否則會發生超出索引範圍的錯誤,使用[]運算子指定索引存取的方式是比較友善,但在某些場合下,使用at()方法會較有效率一些,這涉及Qt的隱式共享機制,稍後再作介紹。

您也可以使用QVector的append()方法來加入元素,使用remove()方法來移除元素,使用insert()方法來插入元素,例如append()的使用如下:

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

或者是使用&lt;&lt;運算子附加元素:

vect &lt;&lt; 5.0 &lt;&lt; 6.0;

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

QList 的子類別QStringList為Qt中應用很廣的類別,可以讓您儲存QString物件,QList的子類別QQueue則提供了佇列結構的容器管理。

以上先列出QVector、QLinkedList及QList的使用比較:

如果想要有連續鄰接的記憶體空間來存放元件,則使用QVector 。

如果需要真正的鏈結資料結構,並使用基於疊代器的存取方式,則使用QLinkedList 。

在大部份情況下,QList 可以滿足快速存取、插入、移除的需求,並可提供基於索引的存取方式。

<code>QList&lt;QString&gt; list;</code>

<code>list &lt;&lt;</code><code>"caterpillar"</code> <code>&lt;&lt;</code><code>"momor"</code> <code>&lt;&lt;</code><code>"bush"</code><code>;</code>

<code>    </code> 

<code>QListIterator&lt;QString&gt; iterator(list);</code>

<code>while</code> <code>(iterator.hasNext()) {</code>

<code>    </code><code>cout &lt;&lt; iterator.next().toAscii().data() &lt;&lt; endl;</code>

  與Java 疊代器類似的,hasNext()測試是否有下一個元素,next()傳回下一個元素,其它還有hasPrevious()、previous()等方法 可以使用。Java風格的疊代器有兩種:唯讀與可讀寫。QListIterator是唯讀疊代器,對於可讀寫疊代器,命名上會加上Mutable,例 如QMutableListIterator,除了next()、previous()等方法之外,還提供了insert()、remove()等方法可 以使用,例如:

<code>QLinkedList&lt;QString&gt; list;</code>

<code> </code><code>list &lt;&lt;</code><code>"caterpillar"</code> <code>&lt;&lt;</code><code>"momor"</code> <code>&lt;&lt;</code><code>"bush"</code><code>;</code>

<code> </code><code>QMutableLinkedListIterator&lt;QString&gt; rwIterator(list);</code>

<code>while</code> <code>(rwIterator.hasNext())</code>

<code> </code><code>{</code>

<code> </code><code>if</code><code>(rwIterator.next() ==</code><code>"momor"</code><code>)</code>

<code> </code><code>{   rwIterator.insert(</code><code>"bee"</code><code>); </code>

<code> </code><code>break</code><code>;</code>

<code>QLinkedListIterator&lt;QString&gt; rIterator(list);</code>

<code>while</code> <code>(</code>

<code>rIterator.hasNext()</code>

<code>)</code>

<code>{ cout &lt;&lt; rIterator.next().toAscii().data() &lt;&lt; endl;</code>

QMap是個有趣的東西,想在裸露的底層C++實作它頗為麻煩。數組建立的是從0開始的連續數字與資料的對應關系,而QMap的作用就是,讓任意一種資料類型對應于另一種資料類型。聲明時如此:QMap&lt;索引類型,資料類型&gt; 變量名。他的表現有點類似于PHP程式設計的array

比如:

<code>#include&lt;QMap&gt;</code>

<code>...</code>

<code>void</code> <code>someFunction()</code>

<code>{</code>

<code>    </code><code>QMap&lt;QString,QString&gt; map;</code>

<code>    </code><code>map[</code><code>"Hello"</code><code>]=</code><code>"World"</code><code>;</code>

<code>    </code><code>QMap&lt;</code><code>int</code><code>,</code><code>double</code><code>&gt; i2d;</code>

<code>    </code><code>i2d[5231]=32.4213;</code>

<code>    </code><code>//周遊比較特殊,得這樣:</code>

<code>    </code><code>QMapIterator&lt;QString,QString&gt; i(map);</code>

<code>    </code><code>while</code><code>(i.hasNext())</code>

<code>        </code><code>doSomething(i.next());</code>

如果我們想用[]操作符通路某一項,但那一項并不存在,那就會自動建立,如果不想建立空白項可以使用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 &lt;int,QString&gt; map;

map[3]="Hello";

map.insert(3,"World");

調用map[3]時,就會得到一個QList&lt;QString&gt;類型的變量。

周遊時依然可用 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&lt;()函數.

他們同樣也是有兩種風格的疊代容器。用來進行周遊的。

STL 風格的

QMap&lt;key,T&gt;  QMap&lt;key,T&gt;::const_iterator QMap&lt;key,T&gt;::iterator//同樣中間那個也是隻讀的,最後那個是讀寫的。下面以一個例子來進行說明:

<code>1.#include &lt;QDebug&gt;</code>

<code>2.</code><code>int</code> <code>main(</code><code>int</code> <code>argc,</code><code>char</code> <code>*argv[])</code>

<code>3.{</code>

<code>4. QMap&lt;QString, QString&gt; map;</code>

<code>5. map.insert(</code><code>"beijing"</code><code>,</code><code>"111"</code><code>);</code>

<code>6. map.insert(</code><code>"shanghai"</code><code>,</code><code>"021"</code><code>);</code>

<code>7. map.insert(</code><code>"tianjin"</code><code>,</code><code>"022"</code><code>);</code>

<code>8. map.insert(</code><code>"chongqing"</code><code>,</code><code>"023"</code><code>);</code>

<code>9. map.insert(</code><code>"jinan"</code><code>,</code><code>"0531"</code><code>);</code>

<code>10. map.insert(</code><code>"wuhan"</code><code>,</code><code>"027"</code><code>);</code>

<code>11.QMap&lt;QString, QString&gt;::const_iterator i;</code>

<code>12.</code><code>for</code><code>( i=map.constBegin(); i!=map.constEnd(); ++i)</code>

<code>13. qDebug() &lt;&lt;i.key() &lt;&lt;</code><code>" "</code> <code>&lt;&lt;i.value();</code>

<code>14. QMap&lt;QString, QString&gt;::iterator mi;</code>

<code>15.mi = map.find(</code><code>"beijing"</code><code>);</code>

<code>16.</code><code>if</code><code>(mi != map.end())</code>

<code>17. mi.value() =</code><code>"010"</code><code>;</code>

<code>18. QMap&lt;QString, QString&gt;::const_iterator modi;</code>

<code>19. qDebug() &lt;&lt;</code><code>""</code><code>;</code>

<code>20.</code><code>for</code><code>( modi=map.constBegin(); modi!=map.constEnd(); ++modi)</code>

<code>21. qDebug() &lt;&lt;modi.key() &lt;&lt;</code><code>" "</code> <code>&lt;&lt;modi.value();</code>

<code>22.</code><code>return</code> <code>0;</code>

<code>23.}</code>

  小結:關于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必須要重載&lt;運算符,因為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() &gt; largestValue) {    largestKey = i.key(); 

largestValue = i.value();    }   }

  Mutable周遊器則可以修改key對應的值:

 QMutableMapIterator i(map); 

while (i.hasNext()) {    i.next();    if (i.value() &lt; 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); 

}   }

本文轉自夜&amp;楓部落格園部落格,原文連結:http://www.cnblogs.com/newstart/archive/2013/05/09/3068625.html,如需轉載請自行聯系原作者