Итераторы в Qt по аналогии с Java
Итераторы по аналогии с Java
Для каждого класса контейнера существуют два типа итераторов по аналогии с Java: один для чтения без возможности изменения и один для чтения и записи.
Примечание: Новый код следует использовать итераторы по стандарту STL, так как они более эффективны и могут использоваться совместно с общими алгоритмами Qt и STL generic algorithms.
| Контейнеры | Итератор для чтения | Итератор для чтения и записи |
|---|---|---|
| QList<T>, QQueue<T>, QStack<T>, | QListIterator<T> | QMutableListIterator<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 и QMap. Типы итераторов для QSet имеют точно такой же интерфейс, как и итераторы QList; аналогично, типы итераторов для QHash имеют тот же интерфейс, что и итераторы QMap.
В отличие от итераторов по стандарту STL, итераторы по аналогии с Java указывают между элементами, а не непосредственно на элементы. По этой причине они указывают либо в самом начале контейнера (перед первым элементом), в самом конце контейнера (после последнего элемента), или между двумя элементами. Диаграмма ниже показывает допустимые позиции итераторов красными стрелками для списка, содержащего четыре элемента:
Вот типичный цикл для перебора всех элементов QList<QString> в порядке и вывода их в консоль:
QList<QString> list;
list << "A" << "B" << "C" << "D";
QListIterator<QString> i(list);
while (i.hasNext())
qDebug() << i.next(); Он работает следующим образом: QList, по которому мы хотим выполнить итерацию, передается в конструктор QListIterator. В этот момент итератор находится перед первым элементом в списке (перед элементом "A"). Затем мы вызываем hasNext() для проверки наличия элемента после итератора. Если есть, мы вызываем next() для перехода к этому элементу. Функция next() возвращает элемент, над которым она переходит. Для QList<QString> этот элемент имеет тип QString.
Вот как выполнить итерацию в обратном порядке по QList:
QListIterator<QString> i(list);
i.toBack();
while (i.hasPrevious())
qDebug() << i.previous(); Код симметричен итерации вперёд, за исключением того, что мы начинаем с вызова toBack() для перемещения итератора после последнего элемента в списке.
Диаграмма ниже иллюстрирует эффект вызова next() и previous() для итератора:
В следующей таблице обобщены функции API QListIterator:
| Функция | Поведение |
|---|---|
| toFront() | Перемещает итератор в начало списка (перед первым элементом) |
| toBack() | Перемещает итератор в конец списка (после последнего элемента) |
| hasNext() | Возвращает true, если итератор не находится в конце списка |
| next() | Возвращает следующий элемент и перемещает итератор на одну позицию вперёд |
| peekNext() | Возвращает следующий элемент без перемещения итератора |
| hasPrevious() | Возвращает true, если итератор не находится в начале списка |
| previous() | Возвращает предыдущий элемент и перемещает итератор на одну позицию назад |
| peekPrevious() | Возвращает предыдущий элемент без перемещения итератора |
QListIterator не предоставляет функций для вставки или удаления элементов из списка во время итерации. Для этого нужно использовать QMutableListIterator. Вот пример, где мы удаляем все нечётные числа из QList<int> с помощью QMutableListIterator:
QMutableListIterator<int> i(list);
while (i.hasNext()) {
if (i.next() % 2 != 0)
i.remove();
} Вызов next() в цикле выполняется каждый раз. Он переходит к следующему элементу в списке. Функция remove() удаляет последний элемент, на котором мы только что перешли. Вызов remove() не делает итератор недействительным, поэтому его безопасно продолжать использовать. Это работает точно так же при итерации в обратном порядке:
QMutableListIterator<int> i(list);
i.toBack();
while (i.hasPrevious()) {
if (i.previous() % 2 != 0)
i.remove();
} Если нам нужно просто изменить значение существующего элемента, мы можем использовать setValue(). В коде ниже мы заменяем любое значение, большее 128, на 128:
QMutableListIterator<int> i(list);
while (i.hasNext()) {
if (i.next() > 128)
i.setValue(128);
} Так же, как и remove(), функция setValue() работает с последним элементом, на котором мы только что перешли. Если мы выполняем итерацию вперёд, это элемент, непосредственно перед итератором; если мы выполняем итерацию назад, это элемент, непосредственно после итератора.
Функция next() возвращает неконстантную ссылку на элемент в списке. Для простых операций нам даже не нужен setValue():
QMutableListIterator<int> i(list);
while (i.hasNext())
i.next() *= 2; Как упоминалось выше, классы итераторов QSet имеют точно такой же API, как и у QList. Теперь мы перейдём к QMapIterator, который несколько отличается, поскольку итерация выполняется по парам (ключ, значение).
Как и QListIterator, QMapIterator предоставляет toFront(), toBack(), hasNext(), next(), peekNext(), hasPrevious(), previous() и peekPrevious(). Компоненты ключ и значение извлекаются путём вызова key() и value() на объекте, возвращаемом функциями next(), peekNext(), previous() или peekPrevious().
Следующий пример удаляет все пары (столица, страна), где название столицы заканчивается на «Город»:
QMap<QString, QString> map;
map.insert("Paris", "France");
map.insert("Guatemala City", "Guatemala");
map.insert("Mexico City", "Mexico");
map.insert("Moscow", "Russia");
...
QMutableMapIterator<QString, QString> i(map);
while (i.hasNext()) {
if (i.next().key().endsWith("City"))
i.remove();
} QMapIterator также предоставляет функции key() и value(), которые работают непосредственно с итератором и возвращают ключ и значение последнего элемента, над которым итератор перешёл. Например, следующий код копирует содержимое QMap в 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());
} Если мы хотим перебрать все элементы с одинаковым значением, мы можем использовать findNext() или findPrevious(). Вот пример, где мы удаляем все элементы с определённым значением:
QMutableMapIterator<int, QWidget *> i(map);
while (i.findNext(widget))
i.remove();
© The Qt Company Ltd
Licensed under the GNU Free Documentation License, Version 1.3.
https://doc.qt.io/qt-6.2/java-style-iterators.html