qt6类QList的两种遍历风格(STL-style-iterators、java-style-iterators)
文章目录
qt6类QList的两种遍历风格
QList provides both STL-style iterators and Java-style iterators
QList同时提供STL风格的迭代器和Java风格的迭代器
Warning: Iterators on implicitly shared containers do not work exactly like STL-iterators. You should avoid copying a container while iterators are active on that container. For more information, read Implicit sharing iterator problem
警告:隐式共享容器上的迭代器的工作方式与STL迭代器不同。当迭代器在容器上处于活动状态时,应避免复制容器。有关更多信息,请阅读隐式共享迭代器问题
Warning: Iterators are invalidated when QList is modified. Consider that all iterators are invalidated by default. Exceptions to this rule are explicitly documented
警告:修改QList时,迭代器无效。假设默认情况下所有迭代器都是无效的。明确记录此规则的例外情况
STL-Style Iterators
STL-style iterators have been available since the release of Qt 2.0. They are compatible with Qt’s and STL’s generic algorithms and are optimized for speed. For each container class, there are two STL-style iterator types: one that provides read-only access and one that provides read-write access. Read-only iterators should be used wherever possible because they are faster than read-write iterators
STL风格的迭代器自Qt2.0发布以来就一直可用。它们与Qt和STL的通用算法兼容,并针对速度进行了优化。对于每个容器类,有两种STL样式的迭代器类型:
一种提供只读访问,另一种提供读写访问。
只读迭代器应该尽可能使用,因为它们比读写迭代器更快
Containers | Read-only iterator | Read-write iterator |
---|---|---|
QList, QStack, QQueue | QList::const_iterator | QList::iterator |
QSet | QSet::const_iterator | QSet::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 |
The API of the STL iterators is modelled on pointers in an array. For example, the ++ operator advances the iterator to the next item, and the operator returns the item that the iterator points to. In fact, for QList and QStack, which store their items at adjacent memory positions, the iterator type is just a typedef for T , and the const_iterator type is just a typedef for const T
STL迭代器的API是以数组中的指针为模型的。例如,++运算符将迭代器前进到下一个项,并且运算符返回迭代器指向的项。事实上,对于将其项存储在相邻内存位置的QList和QStack,迭代器类型只是T的typedef,const_iterator类型只是const T的typedef
In this discussion, we will concentrate on QList and QMap. The iterator types for QSet have exactly the same interface as QList’s iterators; similarly, the iterator types for QHash have the same interface as QMap’s iterators
在本次讨论中,我们将集中讨论QList和QMap。QSet的迭代器类型与QList的迭代程序具有完全相同的接口;类似地,QHash的迭代器类型与QMap的迭代程序具有相同的接口
Here’s a typical loop for iterating through all the elements of a QList<QString> in order and converting them to lowercase
下面是一个典型的循环,用于按顺序遍历QList<QString>
的所有元素,并将它们转换为小写
QList<QString> list = {"A", "B", "C", "D"};
for (auto i = list.begin(), end = list.end(); i != end; ++i)
*i = (*i).toLower();
STL-style iterators point directly at items. The begin() function of a container returns an iterator that points to the first item in the container. The end() function of a container returns an iterator to the imaginary item one position past the last item in the container. end() marks an invalid position; it must never be dereferenced. It is typically used in a loop’s break condition. If the list is empty, begin() equals end(), so we never execute the loop.
STL风格的迭代器直接指向项目。容器的begin()函数返回一个迭代器,该迭代器指向容器中的第一项。容器的end()函数返回一个迭代器,返回到容器中最后一项之后一个位置的假想项。end()标记无效位置;绝不能取消引用。它通常用于循环中断的情况。如果列表为空,begin()等于end(),因此我们从不执行循环
The diagram below shows the valid iterator positions as red arrows for a list containing four items
下图显示了包含四项的列表的有效迭代器位置为红色箭头
Iterating backward with an STL-style iterator is done with reverse iterators
使用STL样式的迭代器反向迭代是使用反向迭代器完成的
QList<QString> list = {"A", "B", "C", "D"};
for (auto i = list.rbegin(), rend = list.rend(); i != rend; ++i)
*i = i->toLower();
In the code snippets so far, we used the unary * operator
to retrieve the item (of type QString) stored at a certain iterator position, and we then called QString::toLower() on it.
For read-only access, you can use const_iterator, cbegin(), and cend(). For example
在迄今为止的代码片段中,我们使用一元运算符*
来检索存储在某个迭代器位置的项(类型为QString),然后对其调用QString::toLower()。
对于只读访问,可以使用const_iterator、cbegin()和cend()。例如
for (auto i = list.cbegin(), end = list.cend(); i != end; ++i)
qDebug() << *i;
The following table summarizes the STL-style iterators’ API
下表总结了STL风格迭代器的API
Expression | Behavior |
---|---|
*i |
Returns the current item |
++i |
Advances the iterator to the next item |
i += n |
Advances the iterator by n items |
--i |
Moves the iterator back by one item |
i -= n |
Moves the iterator back by n items |
i - j |
Returns the number of items between iterators i and j
|
The ++ and - - operators are available both as prefix (++i, - -i) and postfix (i++, i- -) operators.
The prefix versions modify the iterators and return a reference to the modified iterator;
the postfix versions take a copy of the iterator before they modify it, and return that copy.
In expressions where the return value is ignored, we recommend that you use the prefix operators (++i, --i), as these are slightly faster
++和— —运算符可以作为前缀(++i,- -i)和后缀(i++,i- -)运算符使用。
前缀版本修改迭代器并返回对修改后的迭代器的引用;
后缀版本在修改迭代器之前获取迭代器的副本,并返回该副本。
在忽略返回值的表达式中,我们建议您使用前缀运算符(++i,— —i),因为这些运算符会稍微快一些
For non-const iterator types, the return value of the unary * operator
can be used on the left side of the assignment operator.
对于非常量迭代器类型,一元*运算符
的返回值可以在赋值运算符的左侧使用。
For QMap and QHash, the operator returns the value component of an item. If you want to retrieve the key, call key() on the iterator. For symmetry, the iterator types also provide a value() function to retrieve the value. For example, here’s how we would print all items in a QMap to the console
对于QMap和QHash,运算符返回项的值组件。如果要检索键,请在迭代器上调用key()。对于对称性,迭代器类型还提供了一个value()函数来检索值。例如,下面是我们如何将QMap中的所有项目打印到控制台
QMap<int, int> map;
...
for (auto i = map.cbegin(), end = map.cend(); i != end; ++i)
qDebug() << i.key() << ':' << i.value();
Thanks to implicit sharing, it is very inexpensive for a function to return a container per value. The Qt API contains dozens of functions that return a QList or QStringList per value (e.g., QSplitter::sizes()). If you want to iterate over these using an STL iterator, you should always take a copy of the container and iterate over the copy. For example
Thanks to implicit sharing, it is very inexpensive for a function to return a container per value. The Qt API contains dozens of functions that return a QList or QStringList per value (e.g., QSplitter::sizes()). If you want to iterate over these using an STL iterator, you should always take a copy of the container and iterate over the copy. For example
由于隐式共享,函数返回每个值一个容器的成本非常低。Qt API包含数十个函数,每个值返回一个QList或QStringList(例如,QSplitter::sizes())。如果您想使用STL迭代器对其进行迭代,则应始终获取容器的副本并对副本进行迭代。例如
// 对
const QList<int> sizes = splitter->sizes();
for (auto i = sizes.begin(), end = sizes.end(); i != end; ++i)
...
// 错
for (auto i = splitter->sizes().begin();
i != splitter->sizes().end(); ++i)
...
This problem doesn’t occur with functions that return a const or non-const reference to a container
返回对容器的常量或非常量引用的函数不会出现此问题
java-style-iterators
For each container class, there are two Java-style iterator data types: one that provides read-only access and one that provides read-write access
对于每个容器类,都有两种Java风格的迭代器数据类型:一种提供只读访问,另一种提供读写访问
Note: New code should use STL-Style iterators since these are more efficient and can be used together with Qt’s and STL’s generic algorithms
注意:新代码应该使用STL样式迭代器,因为这些迭代器更高效,并且可以与Qt和STL的通用算法一起使用
Containers | Read-only iterator | Read-write iterator |
---|---|---|
QList, QQueue, QStack, | QListIterator | QMutableListIterator |
QSet | QSetIterator | QMutableSetIterator |
QMap<Key, T>, QMultiMap<Key, T> | QMapIterator<Key, T> | QMutableMapIterator<Key, T> |
QHash<Key, T>, QMultiHash<Key, T> | QHashIterator<Key, T> | QMutableHashIterator<Key, T> |
In this discussion, we will concentrate on QList and QMap. The iterator types for QSet have exactly the same interface as QList’s iterators; similarly, the iterator types for QHash have the same interface as QMap’s iterators
在这次讨论中,我们将集中讨论QList和QMap。QSet的迭代器类型与QList的迭代器具有完全相同的接口;类似地,QHash的迭代器类型与QMap的迭代器具有相同的接口
Unlike STL-Style iterators, Java-style iterators point between items rather than directly at items. For this reason, they are either pointing to the very beginning of the container (before the first item), at the very end of the container (after the last item), or between two items. The diagram below shows the valid iterator positions as red arrows for a list containing four items
与STL风格的迭代器不同,Java风格的迭代器指向项之间,而不是直接指向项。因此,它们要么指向容器的最开始(在第一个项目之前),要么指向容器的最末端(在最后一个项目之后),要么指向两个项目之间。下图显示了包含四项的列表的有效迭代器位置为红色箭头
Here’s a typical loop for iterating through all the elements of a QList in order
下面是一个典型的循环,用于按顺序迭代QList<QString>的所有元素
QList<QString> list = {"A", "B", "C", "D"};
QListIterator<QString> i(list);
while (i.hasNext())
QString s = i.next();
It works as follows: The QList to iterate over is passed to the QListIterator constructor. At that point, the iterator is located just in front of the first item in the list (before item “A”). Then we call hasNext() to check whether there is an item after the iterator. If there is, we call next() to jump over that item. The next() function returns the item that it jumps over. For a QList<QString>, that item is of type QString
它的工作原理如下:要迭代的QList被传递给QListIterator构造函数。此时,迭代器正好位于列表中第一项的前面(在项“a”之前)。然后我们调用hasNext()来检查迭代器后面是否有项。如果有,我们调用next()跳过该项。next()函数返回它跳过的项。对于QList<QString>
,该项的类型为QString
QListIterator
Here’s how to iterate backward in a QList
下面是如何在QList中向后迭代
QListIterator<QString> i(list);
i.toBack();
while (i.hasPrevious())
QString s = i.previous();
The code is symmetric with iterating forward, except that we start by calling toBack() to move the iterator after the last item in the list
代码与向前迭代是对称的,只是我们首先调用toBack()将迭代器移动到列表中的最后一项之后
The diagram below illustrates the effect of calling next() and previous() on an iterator
下图说明了在迭代器上调用next()和previous()的效果
The following table summarizes the QListIterator API
下表总结了QListIterator API
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
QListIterator provides no functions to insert or remove items from the list as we iterate. To accomplish this, you must use QMutableListIterator. Here’s an example where we remove all odd numbers from a QList using QMutableListIterator
当我们迭代时,QListIterator没有提供从列表中插入或删除项目的函数。为此,必须使用QMutableListIterator。下面是一个使用QMutableListIterator从QList<int>中删除所有奇数的示例
QMutableListIterator<int> i(list);
while (i.hasNext()) {
if (i.next() % 2 != 0)
i.remove();
}
The next() call in the loop is made every time. It jumps over the next item in the list. The remove() function removes the last item that we jumped over from the list. The call to remove() does not invalidate the iterator, so it is safe to continue using it. This works just as well when iterating backward
循环中的next()调用每次都会进行。它跳过列表中的下一项。函数的作用是删除我们从列表中跳过的最后一项。对remove()的调用不会使迭代器失效,因此继续使用它是安全的。这在向后迭代时也同样有效
QMutableListIterator<int> i(list);
i.toBack();
while (i.hasPrevious()) {
if (i.previous() % 2 != 0)
i.remove();
}
If we just want to modify the value of an existing item, we can use setValue(). In the code below, we replace any value larger than 128 with 128
如果我们只想修改现有项的值,我们可以使用setValue()。在下面的代码中,我们将任何大于128的值替换为128
QMutableListIterator<int> i(list);
while (i.hasNext()) {
if (i.next() > 128)
i.setValue(128);
}
Just like remove(), setValue() operates on the last item that we jumped over. If we iterate forward, this is the item just before the iterator; if we iterate backward, this is the item just after the iterator
就像remove()一样,setValue()对我们跳过的最后一个项目进行操作。如果我们向前迭代,这就是迭代器前面的项;如果我们向后迭代,这就是迭代器后面的项
The next() function returns a non-const reference to the item in the list. For simple operations, we don’t even need setValue
next()函数返回对列表中项的non-const引用
。对于简单的操作,我们甚至不需要setValue
QMutableListIterator<int> i(list);
while (i.hasNext())
i.next() *= 2;
As mentioned above QSet’s iterator classes have exactly the same API as QList’s. We will now turn to QMapIterator, which is somewhat different because it iterates on (key, value) pairs.
如上所述,QSet的迭代器类与QList的具有完全相同的API。我们现在将转向QMapIterator,它有些不同,因为它迭代(键、值)对
Like QListIterator, QMapIterator provides toFront(), toBack(), hasNext(), next(), peekNext(), hasPrevious(), previous(), and peekPrevious(). The key and value components are extracted by calling key() and value() on the object returned by next(), peekNext(), previous(), or peekPrevious().
与QListIterator一样,QMapIterator提供了toFront()、toBack()、hasNext()、next()、peekNext()、hasPrevious()、previous()和peekPrevious()。通过对next()、peekNext()、previous()或peekPrevious返回的对象调用key()和value()来提取键和值组件
The following example removes all (capital, country) pairs where the capital’s name ends with “City”:
以下示例删除首都名称以“City”结尾的所有(capital,country)对
QMap<QString, QString> map = {
{"Paris", "France"},
{"Guatemala City", "Guatemala"},
{"Mexico City", "Mexico"},
{"Moscow", "Russia"}
};
QMutableMapIterator<QString, QString> i(map);
while (i.hasNext()) {
if (i.next().key().endsWith("City"))
i.remove();
}
QMapIterator also provides a key() and a value() function that operate directly on the iterator and that return the key and value of the last item that the iterator jumped above. For example, the following code copies the contents of a QMap into a QHash:
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 we want to iterate through all the items with the same value, we can use findNext() or findPrevious(). Here’s an example where we remove all the items with a particular value:
QMutableMapIterator<int, QWidget *> i(map);
while (i.findNext(widget))
i.remove();