Qt容器类整理

Qt既提供了诸如QVector<T>、QLinkedList<T>和QList<T>等的连续容器,也提供了诸如QMap<K,T>和QHash<K,T>等的关联容器。连续容器存储连续值,关联容器存储键值对。

Qt还提供了在任意容器上执行相关操作的通用算法。例如,qSort()算法对一个连续容器进行排序,qBinaryFind()在经过排序的连续容器上执行一个二进制搜索。

(1)连续容器

      QVector<T>是一种与数组相似的数据结构,它可以把项存储到内存中相邻近的位置。如果能预先知道需要使用多少项,则在定义向量时,就可以初始化向量的大小,并使用[]操作符为它的项赋值;否则,可以稍后重新定义向量的大小,或者在向量的末端增加项。例如:

QVector<double> vect(3);

vect[0]=1.0;

vect[1]=1.99;

vect[2]=12.3445;

或者

QVector<double> vect;

vect.append(1.0);

vect.append(1.99);

vect.append(12.3445);

也可以使用vect<<1.0<<1.99<<12.3445;

而遍历向量的项的方式之一如下:

for(i=0;i<vect.count();i++)

所创建的向量元素如果没有赋以确切的值,就会被使用这个项的类的默认构造函数进行初始化。基本类型和指针类型都会被初始化为0.

对于较大的向量来说,在开头或者中间插入值都是非常耗时的。因此Qt还提供了QLinkedList<T>,这是一种把项存储到内存中不相邻位置的数据结构。链表不支持快速的随机访问,但它提供了常量时间的插入和删除。

链表必须使用迭代器来遍历项。迭代器还可用来指定项的位置,例如,在“Clash”和"Ramones"之间插入字符串"Tote Hosen"

QLinkedList<QString>list;

list.append("Clash");

list.append("Ramones");

QLinkedList<QString>::iterator i=list.find("Ramones");

list.insert(i,"Tote Hosen");

QList<T>连续容器是一个“数组列表”,结合了单一类中QVector<T>和QLinkedList<T>的最重要的优点。它支持随机访问,而且它的界面与QVector一样是基于索引的。在QList<T>的任意一端插入或者移除项都是非常快速的。

值类型T可以是一个与int、double、指针类型、具有默认构造函数的类、赋值构造函数或者赋值操作符相似的类。也可以是一个容器。必须得用空格分开连续的尖括号,例如:

QList<Qvector<double>  >list;

一个容器的值类型也可以是符合之前描述标准的任意自定义类.



对于每个容器类,都有两种Java风格的迭代器类型:只读迭代器和读-写迭代器。只读迭代器有QVectorIterator<T>、QListIterator<T>、QLinkedListIterator<T>.相应的读写迭代器则在其名字中都含有“Mutable”的字样(例如QMutableListIterator<T>)

当使用Java风格的迭代器时,必须牢记的是:它们本身并不是直接指向项的,而是能够定位在第一项之前,最后一项之后或者是两项之间。例如:

QList<double>list;

QListIterator<double>i(list);

while(i.hasNext())

{

     doSomething(i.next());

}

迭代器通过容器遍历来初始化。迭代器在第一项之前就被定位了。如果迭代器的右边有一个项,则调用hasNext()函数会返回true。next()函数返回迭代器右边的项并且将迭代器提升至下一有效位。

向后迭代与此类似,但必须先调用toBack(),以将迭代器定位到最后一项之后的位置。

QList<double>list;

QListIterator<double>i(list);

i.toBack();

while(i.hasPrevious())

{

     doSomething(i.previous());

}


Mutable迭代器在遍历时提供了插入、修改以及删除项的函数。

QMutableListIterator<double>i(list);

while(i.hasNext())

{

      val=i.next();

       if(val<0.0)

       {

               i.setValue(-val);//修改

               i.insert(val+1);//当前项与下一项之间插入

               i.remove();//删除

       }

}

PS:当迭代器遍历完后重新定义迭代器回到起点


除了Java风格迭代器,每一个连续容器类C<T>都有两个STL风格的迭代器类型:C<T>::iterator和C<T>::const_iterator。这两者的区别在于const_iterator不允许修改数据。

容器的begin()函数返回引用容器中的第一项的STL风格的迭代器,而end()函数返回引用最后一个项的之后项的迭代器(例如对一个大小为5的列表取list[5])。如果某个容器为空,则begin()等于end(),这可以用来检验容器是否为空,或者使用isEmpty()函数的方式可更方便达到这个目的。

STL风格的迭代器语法是模仿C++数组指针,我们可以使用++或者--操作符来移动,而使用*来获取当前项。例如:

QList<double> list;
    list<<1.0<<2.2333<<-9.12<<0.2221;
    QList<double>::iterator i;
    for(i=list.begin();i!=list.end();i++)
    {
        *i=qAbs(*i);

    }

一些Qt函数返回一个容器,如果想使用STL风格的迭代器遍历某个函数的返回值,则必须复制此容器并且遍历这个副本。例如:下面给出了如何遍历由QSplitter::sizes()返回的QList<int>的正确方式:

QList<int>list =splitter->sizes();

 QList<double>::iterator i;
    for(i=list.begin();i!=list.end();i++)
    {
        doSomething(*i);

    }

Qt还提供了最后一种在连续容器中遍历项的方式——foreach循环,例如:

QLinkedList<Movie>list;
    foreach (Movie movie,list)
    {
        if(movie.title()=="Citizen")
            qDebug()<<"yes";
    }

foreach伪关键字按照标准的for循环实现。在循环的每一次迭代中,迭代变量(movie)都被设置成一个新值,从容器中的第一项开始向前迭代。foreach循环会在进入循环时自动复制一个容器,因此即使在迭代过程中容器发生变化也不会影响到循环。


(2)关联容器

关联容器可以保存任意多个具有相同类型的项,且它们由一个键索引。Qt提供两个主要的关联容器:QMap<K,T>和QHash<K,T>

QMap<K,T>是一个以升序键存储键值对的数据结构。这种排序可以使它具有良好的查找和插入性能以及键序的迭代。在内部,是以跳跃列表来实现的。

在映射中插入项的操作如下:

QMap<QString, int>map;

map.insert("one",1);

map.insert("two",2);

或者

map["one"]=1;

map["two"]=2;

可以使用value()函数来获得项

int val=map.value("one");//val=1

另外QMap<K,T>中K类型必须提供operator<(),因为它要使用这个操作符以升序顺序存储项。

QMap有一对非常方便的函数keys()和values(),它们在处理小数据集时显得特别有用。它们分别返回映射键的QList和映射值的QList。

映射通常都是单一值得:如果赋予一个现有的键一个新值,则原有的旧值将被该新值取代,以确保两个项不会共有同一个键。通过使用insertMulti()函数或者QMultiMap<K,T>,可以让多个键值对应相同的键。例如

QMap <QString,int> map;
    map.insert("one",1);
    map.insertMulti("one",2);
    map.insertMulti("one",3);
    QList<int> list;
    
 
    list = map.values("one");
    int val=map.value("one");
    QListIterator<int> i(list);
    while(i.hasNext())
    {
        qDebug()<<i.next();//输出3  2  1
    }


QHash<K,T>是一个在哈希表中存储键值对的数据结构。它的接口几乎与QMap相同,但是它对K模板类型有不同的要求,而且它提供了更快的查找功能。

QHash中K的值类型还需要提供一个operator==(),并需要一个能够为键返回哈希值的全局qHash()函数的支持。Qt已经为qHash()函数提供了整型,指针型,QChar,QString以及QByteArray.

QHash为它内部的哈希表自动分配最初的存储区域,并在有项被分配或删除时重新划分所分配的存储区域大小。也可以调用reserve()或者squeeze()来指定或者压缩希望存储到哈希表中的项的数目,以进行性能调整。通常的做法是利用我们预期的最大的项的数目来调用reserve(),然后插入数据,最后如果有多出的项,则调用squeeze()以使内存的使用减到最小。

虽然哈希表通常都是单一值的,但是使用insertMulti()函数或者MultiHash<K,T>,也可以将多个值赋给同一个键。

最简便的遍历存储在关联容器中所有键值对的方式是使用Java风格的迭代器,因为迭代器必须能同时访问键和值,针对关联容器的Java风格的迭代器和连续容器的运作方式有些差异。主要在于next()和previous()返回一个代表键值对的对象,而不是一个简单的值。我们可以用key()和value()分别从这个对象中获得键和值。例如:

QMap<QString,int>map;
    map.insert("one",1);
    map.insert("two",2);
    map.insert("three",3);
    QMapIterator<QString,int>i(map);
    int sum=0;
    while(i.hasNext())
    {
        sum+=i.next().value();
    }
    qDebug()<<sum;//输出6

STL风格的迭代器也提供了key()和value()函数。foreach循环也可以用在关联容器中,但是它仅对键值对上的值分量有效。如果同时需要项中的值和键,可以在如下的嵌套式foreach循环中调用keys()和values(const K&)函数:

foreach(QString key,map.keys())

{

       foreach(int value,map.values(key))

       {

               doSomething(key,value);

        }

}

(3)通用算法

<QtAlgorithmn>的头文件声明了在容器类上实现基本算法的一套全局模板函数。这些函数中的大部分都是在STL风格上的迭代器上工作的。

qFind()算法在容器中查找一个特定的值。它接受一个“begin”和一个“end”迭代器,并且返回一个与其匹配的指向第一项的迭代器;如果没有匹配的项,则返回“end”,在下面的例子中,将i设置为begin()+1,将j设置为end():

QStringList list;

list<<"A"<<"B"<<"C"<<"D";

QStringList::iterator  i=qFind(list.begin(),list.end(),"B");

QStringList::iterator  J=qFind(list.begin(),list.end(),"E");

qBinaryFind()算法执行的搜索操作与qFind类似,其区别在于qBinaryFind()算法假设项都是以升序的顺序存储的,并且使用了快速二分搜索而不是qFind()算法的线性搜索。

qFill()算法采用了一个特定的值初始化一个容器:

QLinkedList<int> list(10);

qFill(list.begin(),list.end(),1009);

qCopy()算法将一个容器类的值复制到另一个容器类中:

QVector<int>list;

list<<1<<2<<3;

QVector<int>vect(list.count());

qCopy(list.begin(),list.end(),vect.begin());

qCopy()页可以用来在同一个容器中复制值,只要数据来源范围与目标范围不重叠。例如:

qCopy(list.begin(),list.begin()+2,list.end()-2);

qSort()算法则以升序排列容器中的项:

qSort(list.begin(),list.end());

默认情况下,qSort()使用<操作符对项进行排序。为了升序排序,我们将qGreater<T>()作为第三个参数来传递(其中T为容器类的值类型),过程如下:

qSort(list.begin(),list.end(),qGreater<int>());

可以用第三个参数来定义用户的排序标准,例如:

bool text(const QString &a,const QString &b)

{

      return a.toLower()<b.toLower();

}

qSort(list.begin(),list.end(),text);

qStableSort()算法与qSort()很相似,但qStableSort()算法还可以保证进行对等比较的项在排序之后表现与之前相同的排序。

qDeleteAll()算法对每一个存储在容器类中的指针调用delete。这仅对于那些为指针类型的容器类才有意义的,在调用之后,这些项仍然作为悬摆指针存在于容器类中上,因此我们通常也应该在容器类上调用clear(),例如:

qDeleteAll(list);

list.clear();

qSwap()算法可以交换两个变量的值,例如:

int x1=line.x1();

int x2=line.x2();

if(x1>x2)

qSwap(x1,x2);

最后,还有返回参数绝对值的qAbs()函数、以及返回最大、最小值得qMax()、qMin()函数。

(4)字符串、字节数组和变量

QString、QByteArray和QVariant这三种类与容器类在许多方面都有相同之处,在一些情况下都可以代替容器类来使用。

QString类支持16位Unicode值,从其他字符串或者数组来建立一个字符串的另一种方法是使用arg():

str=QString("%1 %2 (%3s-%4s)").arg("A").arg("b").arg(1990).arg(1900);

通过使用QString::number,可以将数字转化为字符串,还可以使用toInt(),toDouble,toLongLong()来完成从字符串到数字的逆转换。

一旦有了字符串,我们通常都希望从字符串中提取出所需的部分。mid()函数返回在给定位置(第一个参数)开始且达到给定长度(第二个参数)的子串。例如:

QString  str=“Hello World!”

qDebug()<<str,mid(6,5);//输出World

如果省略第二个参数,则输出从给定位置到结尾的子串。也可以用left或者right来完成相似的任务,它们接受字符的数量n,并且返回前n个或者后n个字符。

如果想查明一个字符串是否包含一个特定的字符,子串或者正则表达式,可以使用QString中的indexOf()函数,它返回查找到的第一项的位置,失败返回-1:

QString str="Hello World!";

int i=str.indexOf("Hello");//这会将i设置为0

如果仅仅想要检查字符串是否以某个字符(串)开始或者结束,则可以使用startsWith()和endsWith()函数,利用==操作符进行比较是区分大小写的,如果想不区分大小写,可以使用toUpper()或toLower()函数。

如果想使用一个字符串来代替另一个字符串中的某一部分,可以使用replace():

QString  str=“Hello World”;

str.replace(1,4,HHHH);//str="HHHHH World"

上面代码可以用remove()和insert()写成:

str.remove(1,4);

str.insert(1,"HHHH");

trimmed()可以去除字符串两端的空白,simplified()可以用空格符代替连续空白处

使用split()可以把一个字符串分成一些QStringList子串

QString str=“Hello World”;

QStringList list=str.split(" ");//str分成了Hello和World子串,并存储在list中

使用join()函数,QStringList中的项还可以连接起来形成一个单一的字符串。在每一对被连接的字符串之间都要插入join()的参数,例如:

QStringList list;

list<<"Hello"<<"World";

str=list.join("\n");//str="Hello\nWorld"

大多数情况下,从const char*字符串到QString的转换是自动完成的。

要将QString转换为const char*,可以使用toAscii()或toLatin1()函数,它们返回QByteArray,而利用QByteArray::data()或QByteArray::constData(),可以将QByteArray转换为const char*,例如:

printf("User %s\n",str.toAscii().data());

当在QByteArray上调用data()或者constData()时,返回的字符串属于QByteArray对象。QByteArray类有一个和QString很相似的应用编程接口。诸如left(),right(),mid(),toLower(),toUpper(),trimmed(),simplified()等函数。QByteArray对于存储原始的二进制数据以及8位编码的文本字符串非常有用。一般说来推荐使用QString,

因为QString支持Unicode编码。

QVariant类可以支持很多种Qt类型的值,除了基本的c++数字类型,还包括QBrush、QColor、QCursor、QDateTime、QFont、QKeySequence、QPalette、QPen、QPixmap、QPoint、QRect、QRegion、QSize和QString。也支持容器类,如Map<QString,QVariant>,QStringList和QList<QVariant>.

你可能感兴趣的:(qt,容器,QLIst,关联容器,连续容器)