QMap — это отсортированная по ключу ассоциативная коллекция из Qt, реализованная как сбалансированное дерево. Очень часто требуется обойти все её элементы — по ключам, по значениям, или по парам ключ–значение. В этой статье мы рассмотрим различные способы перебора значений в QMap, включая плюсы, минусы и советы по применению.
1. Классический перебор через итераторы
QMap<QString, int> map;
map["Alice"] = 10;
map["Bob"] = 20;
map["Carol"] = 30;
QMap<QString, int>::iterator i;
for (i = map.begin(); i != map.end(); ++i) {
qDebug() << i.key() << ":" << i.value();
}
Плюсы:
- Полный контроль над итерацией (можно изменять элементы).
- Высокая производительность (по сравнению с
.keys()и.values()).
Минусы:
- Не очень удобочитаемо.
- Требуется аккуратно обращаться с типами итераторов.
2. Использование const_iterator
Если вы не собираетесь изменять значения, лучше использовать const_iterator:
QMap<QString, int>::const_iterator i;
for (i = map.constBegin(); i != map.constEnd(); ++i) {
qDebug() << i.key() << ":" << i.value();
}
Плюсы:
- Безопаснее: гарантирует, что вы не измените карту.
- Производительный и стильный способ для чтения.
3. Использование foreach (устаревший, но всё ещё встречается)
foreach (QString key, map.keys()) {
qDebug() << key << ":" << map.value(key);
}
Минусы:
map.keys()создаёт временныйQList<QString>, что требует дополнительных затрат.value(key)делает поиск по ключу — это О(log N) операция, и если коллекция большая, производительность может резко упасть.
🚫 Qt официально не рекомендует использовать foreach с контейнерами Qt в новых проектах (начиная с Qt 5.7).
4. Современный способ: range-based for
for (auto it : map.toStdMap()) {
qDebug() << QString::fromStdString(it.first) << ":" << it.second;
}
Или через итератор напрямую:
for (auto it = map.constBegin(); it != map.constEnd(); ++it) {
qDebug() << it.key() << ":" << it.value();
}
Плюсы:
- Удобочитаемость.
- Поддерживает
const,auto, иstructured bindingsв C++17.
5. Использование std::for_each
std::for_each(map.constBegin(), map.constEnd(), [](const QMap<QString, int>::value_type& pair) {
qDebug() << pair.first << ":" << pair.second;
});
Удобно для использования с лямбда-выражениями и функциональным стилем.
6. Перебор только ключей или значений
for (const QString &key : map.keys()) {
qDebug() << key;
}
for (int value : map.values()) {
qDebug() << value;
}
Оба метода создают временные QList, что может быть затратным для больших карт.
7. Перебор с structured bindings (C++17)
Если вы работаете со std::map или конвертируете QMap в std::map:
for (const auto& [key, value] : map.toStdMap()) {
qDebug() << QString::fromStdString(key) << ":" << value;
}
8. Какой способ выбрать?
| Способ | Изменение данных | Быстродействие | Рекомендуется? |
|---|---|---|---|
iterator | Да | ✅ Высокое | ✅ Да |
const_iterator | Нет | ✅ Высокое | ✅ Да |
foreach map.keys() | Нет | ❌ Низкое | 🚫 Нет |
range-based for | Нет | ✅ Высокое | ✅ Да (современный) |
map.keys() / .values() | Нет | ❌ Среднее–низкое | ⚠️ Ограниченно |
std::for_each | Нет | ✅ Высокое | ✅ Для C++-стиля |
Практический совет
Если вам нужно часто перебирать элементы и производительность критична — используйте const_iterator или range-based for. Избегайте map.keys() и value(key) в цикле, особенно на больших объемах данных.
Пример на практике: фильтрация элементов
for (auto it = map.constBegin(); it != map.constEnd(); ++it) {
if (it.value() > 50) {
qDebug() << "Найдено:" << it.key() << ":" << it.value();
}
}
Заключение
QMap предоставляет гибкие возможности для перебора, и выбор подхода зависит от ваших целей: удобство, безопасность, производительность. Не забывайте учитывать стоимость операций и использовать const там, где это возможно.
