C++的标准模板库(STL)中定义了一系列容器,并对应有若干容器类和操作容器用到的迭代器(这部分内容可参见本人博客《C++学习笔记——STL标准模板库》)。Qt自定义了几个容器类,这些容器类同样是基于模板的,可以实例化为具体的数据类型。相比于STL中的容器,Qt中的容器更加轻巧、安全和便于使用,而且进行了速度和存储优化,另外,它们还是线程安全的,可以作为只读容器被多个线程访问。
与STL类似,Qt的容器类分为顺序容器和关联容器。前者的数据结构是线性表,后者的数据结构是树。顺序容器类有QList,QLinkedList,QVector,QStack,QQueue;关联容器类包括QMap,QMultiMap,QHash,QMultiHash,Qset。同样使用迭代类访问容器中的数据项,Qt提供Java类型的迭代类和STL类型的迭代类。Java类型的迭代易于适用并提供高级功能,而STL类型的迭代效率则会更高一些。
容器
QList
Qlist是最常用的容器类,它存储一组类型为T的数据。其内核实现是用数组列表的方式进行存储的,这使得用下标方式获取某个值非常快。对Qlist进行增删改查的常用接口有:
接口 |
描述 |
insert() |
插入数据 |
removeAt() |
删除某个数据 |
removeFirst() |
删除头
扫描二维码关注公众号,回复:
9657328 查看本文章
|
removeLast() |
删除尾 |
repalce() |
更改数据 |
move() |
移动数据 |
swap() |
交换数据 |
append |
在头前增加数据 |
prepend |
在尾后增加数据 |
at() |
快速获取某位置数据,类似数据下标 |
isEmpty() |
判断容器是否为空 |
size() |
判断容器数据大小 |
QList<int> integerList;
QList<QDate> dateList;
QLinkedList
QLinkedList 是链表,很像QList,但是数据项不是用连续的内存存储的,它基于迭代器访问数据项,且插入和删除数据项的操作时间相同。除了不提供基于下标索引的数据项访问外,QLinkedList的其他接口函数与QList基本相同。
QLinkedList<int> integerList;
QLinkedList<QTime> timeList;
QVector
Qvector提供动态数组的功能,以下标索引访问数据。QVector与QList的函数接口几乎完全相同,但是由于QVector的数据项是连续存储的,在其头或者中间位置插入数据的效率可能会非常慢。
QVector<int> integerVector;
QVector<QString> stringVector;
QStack
QStack是类似于堆栈的后入先出LIFO容器类。它的基本接口是push()和pop()。
QStack<int> stack;
stack.push(1);
stack.push(2);
stack.push(3);
while (!stack.isEmpty())
cout << stack.pop() << endl;
QQueue
Queue是类似于队列的先入先出FIFO容器类。它的基本接口是enqueue()和dequeue()。
QQueue<int> queue;
queue.enqueue(1);
queue.enqueue(2);
queue.enqueue(3);
while (!queue.isEmpty())
cout << queue.dequeue() << endl;
QSet
QSet是单个数据的集合。基于散列表,存储的数据顺序是不定的。查找值的速度非常快。
QSet<QString> set;
set.insert("one");
set.insert("three");
set.insert("seven");
if (!set.contains("ninety-nine"))
QMap
QMap<Key, T> 提供一个关联数组,一个键(key)映射到一个(key)。QMap存储数据是按照键的顺序,也即它会自动排序。如果我们不需要排序,QHash会更好用。
QMap<QString, int> map;
map["one"] = 1;
map["three"] = 3;
map["seven"] = 7;
map.insert("twelve", 12);
int num1 = map["thirteen"];
int num2 = map.value("thirteen");
QMultiMap
QMultiMap<Key, T> T是QMap的子类,用于处理多值映射(多个值对应一个键值)。注意,QMultiMap不提供[]操作符,可只用value和values获取值,返回值是QList<T>。
QMultiMap<QString, int> map1, map2, map3;
map1.insert("plenty", 100);
map1.insert("plenty", 2000);
// map1.size() == 2
map2.insert("plenty", 5000);
// map2.size() == 1
map3 = map1 + map2;
// map3.size() == 3
QList<int> values = map.values("plenty");
for (int i = 0; i < values.size(); ++i)
cout << values.at(i) << endl;
QHash
QHash<Key, T>是基于散列表来实现字典功能的模板类,与QMap的用法和功能基本相同,但QHash查找速度更快,数据项不会自动排序。
QHash<QString, int> hash;
hash["one"] = 1;
hash["three"] = 3;
hash["seven"] = 7;
hash.insert("twelve", 12);
int num1 = hash["thirteen"];
int num2 = hash.value("thirteen");
QMultiHash
QMultiHash是QHash的子类,用于处理多值映射。其用法与QHash类似。同样,QMultiHash不提供[]操作符,可只用value和values获取值,返回值是QList<T>。
QMultiHash<QString, int> hash1, hash2, hash3;
hash1.insert("plenty", 100);
hash1.insert("plenty", 2000);
// hash1.size() == 2
hash2.insert("plenty", 5000);
// hash2.size() == 1
hash3 = hash1 + hash2;
// hash3.size() == 3
QList<int> values = hash.values("plenty");
for (int i = 0; i < values.size(); ++i)
cout << values.at(i) << endl;
迭代器
Java类型迭代器
对于每个容器类都有两个Java类型迭代器,一个用于只读操作,另一个用于读写操作。
Containers |
Read-only iterator |
Read-write iterator |
QList<T>, QQueue<T> |
QListIterator<T> |
QMutableListIterator<T> |
QLinkedList<T> |
QLinkedListIterator<T> |
QMutableLinkedListIterator<T> |
QVector<T>, QStack<T> |
QVectorIterator<T> |
QMutableVectorIterator<T> |
QSet<T> |
QSetIterator<T> |
QMutableSetIterator<T> |
QMap<Key, T>, QMultiMap<Key, T> |
QMapIterator<Key, T> |
QMutableMapIterator<Key, T> |
QHash<Key, T>, QMultiHash<Key, T> |
QHashIterator<Key, T> |
QMutableHashIterator<Key, T> |
对于像QList的顺序容器类,Java类型迭代器的指针不是指向一个数据项,而是在数据项之间。
QList<QString> list;
list << "A" << "B" << "C" << "D";
QListIterator<QString> i(list);
while (i.hasNext())
qDebug() << i.next();
对于上述只读遍历,容器list作为参数传给迭代器i的构造函数,开始迭代器的指针指向list第一个数据项的前面(A的前面),调用hasNext()函数以判断迭代器后面是否还有数据项。如果有,就调用next跳过一个数据项,且next返回跳过去的那个数据项。也可以反向遍历:
QListIterator<QString> i(list);
i.toBack();
while (i.hasPrevious())
qDebug() << i.previous();
常见的移动指针和读取数据的迭代器接口有:
Function |
Behavior |
toFront() |
Moves the iterator to the front of the list (before the first item) |
toBack() |
Moves the iterator to the back of the list (after the last item) |
hasNext() |
Returns true if the iterator isn't at the back of the list |
next() |
Returns the next item and advances the iterator by one position |
peekNext() |
Returns the next item without moving the iterator |
hasPrevious() |
Returns true if the iterator isn't at the front of the list |
previous() |
Returns the previous item and moves the iterator back by one position |
peekPrevious() |
Returns the previous item without moving the iterator |
上述为只读迭代器,若要对数据进行修改,则需要使用QMutableListIterator。如:
QMutableListIterator<int> i(list);
while (i.hasNext())
{
if (i.next() > 128)
i.setValue(128);
}
对于关联容器QMap<key T>,使用QMapIterator和QMutableMapIterator迭代器类。他们具有上表中所有的接口,并增加了key()和value()函数用以获取刚刚跳过的数据项的键和值。如果在多值容器中遍历,可以用findNext或findPrevious函数查找下一个或上一个值。
QMap<int, QWidget *> map;
QHash<int, QWidget *> hash;
QMapIterator<int, QWidget *> i(map);
while (i.hasNext())
{
i.next();
hash.insert(i.key(), i.value());
}
//if there are multi values
QMutableMapIterator<int, QWidget *> i(map);
while (i.findNext(widget))
i.remove();
STL类型迭代器
STL迭代器与Qt和STL兼容,并且进行了速度优化。对于每个容器类都有两个STL类型迭代器,一个用于只读操作,另一个用于读写操作。若无须修改数据时一定要使用只读迭代器,因为它们速度更快。
Containers |
Read-only iterator |
Read-write iterator |
QList<T>, QQueue<T> |
QList<T>::const_iterator |
QList<T>::iterator |
QLinkedList<T> |
QLinkedList<T>::const_iterator |
QLinkedList<T>::iterator |
QVector<T>, QStack<T> |
QVector<T>::const_iterator |
QVector<T>::iterator |
QSet<T> |
QSet<T>::const_iterator |
QSet<T>::iterator |
QMap<Key, T>, QMultiMap<Key, T> |
QMap<Key, T>::const_iterator |
QMap<Key, T>::iterator |
QHash<Key, T>, QMultiHash<Key, T> |
QHash<Key, T>::const_iterator |
QHash<Key, T>::iterator |
STL类型迭代器是数组的指针,所以可以只用++ -- += -=运算符移动位置用解除引用运算符*获取其值。与JAVA类型迭代器不同,STL类型迭代器直接指向数据项。
begin()函数使迭代器指向容器的第一个数据项,end()函数使函数指向一个虚拟的标识结尾的数据项。end()表示的数据项是无效的,一般用作循环结束的判断条件。
对于QList等顺序容器,其通常用法为:
QList<QString>::const_iterator i;
for (i = list.constBegin(); i != list.constEnd(); ++i)
qDebug() << *i;
对于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关键字
这里还要提到上篇笔记QtGlobal头文件中定义了一个foreach关键字,其作用为遍历容器中所有的项。语法为:
foreach (variable, container)
statement.
可以看到使用foreach比使用迭代器更加简洁,像是一个for循环,对于多值映射可以用两层foreach来实现。
QLinkedList<QString> list;
...
foreach (const QString &str, list)
qDebug() << str;
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;
}