Итераторы Qt в стиле Java
Итераторы в стиле Java
Для каждого класса контейнера существуют два типа данных итераторов в стиле Java: один, обеспечивающий чтение только для чтения, и один, обеспечивающий чтение и запись.
Примечание: Новый код должен использовать итераторы в стиле STL, поскольку они более эффективны и могут использоваться вместе с общими алгоритмами Qt и STL .
| Контейнеры | Итератор только для чтения | Итератор для чтения и записи |
|---|---|---|
| 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.0/java-style-iterators.html