Готовые методы реализаций

Удаление нескольких элементов из QList при помощи QMutableListIterator

обновлено 847 просмотров

Назначение

В QList есть и итераторы в стиле Java, и итераторы в стиле STL:

ХарактеристикаJava-стильSTL-стиль
УровеньВысокоуровневыеНизкоуровневые
ПростотаПроще в использованииТребуют больше внимания
ЭффективностьНемного менее эффективныБолее эффективны
ПозицияУказывают между элементамиУказывают непосредственно на элемент

Альтернативой использованию итераторов является работа с индексами. Большинство функций-членов QList принимают индекс в качестве первого параметра, что позволяет получать доступ, вставлять и удалять элементы без использования итераторов.

Класс QMutableListIterator<T> позволяет перебирать QList<T> и изменять список прямо во время обхода. Если модификация не нужна, следует использовать чуть более быстрый QListIterator<T>.

Конструктор QMutableListIterator принимает QList в качестве аргумента. После построения итератор находится в самом начале списка.

Как работает итератор

Функция next() возвращает следующий элемент в списке и продвигает итератор. В отличие от STL-итераторов, Java-итераторы указывают между элементами, а не непосредственно на элементы:

  • Первый вызов next() переводит итератор в положение между первым и вторым элементом и возвращает первый элемент.
  • Второй вызов next() продвигает итератор между вторым и третьим элементом, возвращая второй элемент.
  • И так далее.

Пример удаления элементов

Фрагмент кода, который удаляет все значения больше 20:

QList<int> list;
list << 2 << 11 << 54 << 3 << 6 << 2 << 76 << 11 << 3 << 2;

QMutableListIterator<int> i(list);
while (i.hasNext()) {
    int currentValue = i.next();
    if (currentValue > 10)
        i.remove();
}

qDebug() << list;

До:

QList(2, 11, 54, 3, 6, 2, 76, 11, 3, 2)

После:

QList(2, 3, 6, 2, 3, 2)

Перебор в обратном порядке

Для обхода списка от конца к началу используется toBack() и previous():

QMutableListIterator<int> i(list);
i.toBack();
while (i.hasPrevious())
    qDebug() << i.previous();

Метод toBack() перемещает итератор за последний элемент, а previous() возвращает элементы в обратном порядке, продвигая итератор к началу.

Ключевые методы QMutableListIterator

МетодНазначение
hasNext()Есть ли следующий элемент
next()Вернуть следующий элемент и продвинуть итератор
hasPrevious()Есть ли предыдущий элемент
previous()Вернуть предыдущий элемент и откатить итератор
remove()Удалить текущий элемент
setValue()Заменить текущий элемент
insert()Вставить элемент в текущую позицию
toFront()Переместить итератор в начало
toBack()Переместить итератор в конец
findNext() / findPrevious()Найти элемент начиная с текущей позиции

Важные замечания

  1. remove() удаляет элемент, возвращённый последним next() или previous(). Его нельзя вызывать до первого вызова next() — иначе поведение неопределённо.
  2. Не используйте STL-итераторы при удалении. После erase() они инвалидируются, что требует ручной корректировки. Java-итераторы решают эту проблему автоматически.
  3. QMutableListIterator изменяет оригинальный список — копии не создаются, все изменения применяются напрямую.
  4. Для QVectorQLinkedList и других контейнеров существуют аналогичные классы: QMutableVectorIteratorQMutableLinkedListIterator и т. д.
  5. Для простых случаев без модификации используйте индексы или range-based for — это самый быстрый и читаемый вариант:cppfor (int value : list) { qDebug() << value; }
  6. Удаление в цикле по индексу требует осторожности — после удаления индексы сдвигаются:cppfor (int i = 0; i < list.size(); ) { if (list[i] > 10) list.removeAt(i); else ++i; }Java-итераторы избавляют от этой ручной работы.
  7. В QList стоимость удаления из середины выше, чем в QLinkedList — если предполагается много удалений, стоит рассмотреть другой контейнер.

Итог

QMutableListIterator<T> — удобный инструмент для перебора и модификации QList, особенно при удалении элементов в цикле. Java-итераторы работают на более высоком уровне абстракции, чем STL, и избавляют от ошибок, связанных с инвалидацией указателей и сдвигом индексов. Их главный недостаток — чуть меньшая производительность, которая в большинстве задач несущественна. Если модификация не требуется, стоит выбрать QListIterator<T> или обычный обход по индексам.