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

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

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

Назначение

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

Итераторы в стиле Java vs STL

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

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

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

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

Конструктор QMutableVectorIterator принимает QVector в качестве аргумента. После построения итератор находится в самом начале списка — перед первым элементом.

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

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

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

Демонстрация удаления всех элементов, значение которых больше 10:

QVector<int> vector;
vector << 0 << 11 << 33 << 53 << 8 << 3 << 42 << 4 << 80 << 2;
qDebug() << vector;

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

qDebug() << vector;

В итоге удалятся все элементы больше 10:

До:

QVector(0, 11, 33, 53, 8, 3, 42, 4, 80, 2)

После:

QVector(0, 8, 3, 4, 2)

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

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

QMutableVectorIterator<int> i(vector);
i.toBack();
while (i.hasPrevious())
    qDebug() << i.previous();

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

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

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

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

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

Итог

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