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();

参考资料

stl-style-iterators
java-style-iterators