もう迷わない!Qt QMap::Iteratorのエラーとトラブルシューティング

2025-05-27

QMap::Iterator とは

QMap::Iterator は、Qt の QMap コンテナクラスに格納された要素(キーと値のペア)を順番にたどっていくための「イテレータ」と呼ばれるものです。C++ の標準ライブラリ(STL)にあるイテレータと似たスタイルで利用できます。

QMapとは? QMap は、キーと値のペアを格納する連想コンテナです。キーによって値に高速にアクセスできるのが特徴です。QMap では、キーは常にソートされた順序で保持されます。

QMap::Iterator の主な特徴

  • キーによるソート: QMap はキーの順序で要素を格納するため、QMap::Iterator で反復処理を行うと、キーの昇順で要素が取得されます。
  • 初期化: QMap::Iterator をデフォルトコンストラクタで作成した場合、未初期化状態です。使用する前に、QMap::begin()QMap::end()QMap::find() などの QMap の関数を使って初期化する必要があります。
  • 非 const イテレータ: QMap::Iterator は、イテレータが指す要素の「値」を変更できます。もし値を変更する必要がなく、読み取り専用でアクセスしたい場合は、QMap::const_iterator を使うべきです。const_iterator はより高速で、コードの可読性も向上させます。
  • STLスタイル: C++ 標準ライブラリの std::map::iterator と同様の使い方ができます。

使用例

典型的な QMap::Iterator を使ったループの例を見てみましょう。

#include <QCoreApplication>
#include <QMap>
#include <QDebug> // qDebug() を使用するために必要

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    QMap<QString, int> map;
    map.insert("January", 1);
    map.insert("February", 2);
    map.insert("March", 3);
    map.insert("April", 4);

    // QMap::Iterator を使ってすべての要素を反復処理し、キーと値を出力
    qDebug() << "--- マップの要素(読み取り) ---";
    QMap<QString, int>::iterator i;
    for (i = map.begin(); i != map.end(); ++i) {
        qDebug() << i.key() << ":" << i.value();
    }

    // QMap::Iterator を使って値を変更
    qDebug() << "\n--- マップの要素(値の変更) ---";
    for (i = map.begin(); i != map.end(); ++i) {
        if (i.key() == "March") {
            i.value() = 30; // 値を変更
        }
    }

    // 変更後の要素を出力
    for (i = map.begin(); i != map.end(); ++i) {
        qDebug() << i.key() << ":" << i.value();
    }

    // QMap::Iterator を使って要素を削除
    qDebug() << "\n--- マップの要素(削除) ---";
    i = map.begin();
    while (i != map.end()) {
        if (i.key().startsWith("A")) { // "A" で始まるキーの要素を削除
            i = map.erase(i); // erase() は次の要素へのイテレータを返す
        } else {
            ++i;
        }
    }

    // 削除後の要素を出力
    for (i = map.begin(); i != map.end(); ++i) {
        qDebug() << i.key() << ":" << i.value();
    }

    return a.exec();
}

出力例

--- マップの要素(読み取り) ---
"April": 4
"February": 2
"January": 1
"March": 3

--- マップの要素(値の変更) ---
"April": 4
"February": 2
"January": 1
"March": 30

--- マップの要素(削除) ---
"February": 2
"January": 1
"March": 30

ポイント

  • map.erase(i) は、イテレータ i が指す要素を削除し、削除された要素の次の要素を指すイテレータを返します。
  • map.end() はマップの最後の要素の次を指すイテレータを返します。(実際の要素を指しているわけではない点に注意してください。ループの終了条件として使われます。)
  • map.begin() はマップの最初の要素を指すイテレータを返します。
  • ++i または i++ で次の要素に進みます。
  • i.value() で現在の要素の値にアクセスします。QMap::Iterator の場合、これは変更可能な参照を返すため、i.value() = new_value; のように値を変更できます。
  • i.key() で現在の要素のキーにアクセスします。

前述の通り、QMap::Iterator は値を変更できますが、QMap::const_iterator は値を変更できません。読み取り専用の操作では const_iterator を使うのが一般的です。

// QMap::const_iterator の使用例
QMap<QString, int>::const_iterator ci;
for (ci = map.constBegin(); ci != map.constEnd(); ++ci) {
    qDebug() << ci.key() << ":" << ci.value();
    // ci.value() = 100; // これはコンパイルエラーになります
}

Qt には、STL スタイルのイテレータ(QMap::iteratorQMap::const_iterator)の他に、Java スタイルのイテレータ(QMapIteratorQMutableMapIterator)も存在します。Java スタイルのイテレータは、より高レベルで使いやすい API を提供しますが、STL スタイルのイテレータの方がわずかに高速です。



QMap::Iterator はC++のイテレータの概念に基づいているため、STL(Standard Template Library)のイテレータと同様の注意点があります。

イテレータが無効になった後にアクセスする (Dangling Iterator)

これは最も一般的で危険なエラーの一つです。QMap の要素が削除されたり、QMap 自体が変更されたりすると、そのマップに対する既存のイテレータが無効になることがあります。無効になったイテレータにアクセスしようとすると、未定義の動作(クラッシュ、メモリ破損など)が発生します。

エラー例

QMap<int, QString> myMap;
myMap.insert(1, "One");
myMap.insert(2, "Two");

QMap<int, QString>::iterator it = myMap.begin();
myMap.clear(); // マップがクリアされ、it が無効になる

qDebug() << it.key(); // 無効なイテレータへのアクセス!

トラブルシューティング

  • マップが変更される可能性のある操作に注意する
    insert()clear() など、マップの内部構造を変更する可能性のある操作の前後で、イテレータの有効性を再確認するか、イテレータを再取得することを検討してください。

  • 要素の削除後にイテレータを更新する
    QMap::erase() メソッドは、削除された要素の次の要素を指す有効なイテレータを返します。ループ内で要素を削除する場合は、この戻り値を使ってイテレータを更新する必要があります。

    QMap<int, QString> myMap;
    myMap.insert(1, "One");
    myMap.insert(2, "Two");
    myMap.insert(3, "Three");
    
    QMap<int, QString>::iterator it = myMap.begin();
    while (it != myMap.end()) {
        if (it.key() == 2) {
            it = myMap.erase(it); // erase() の戻り値でイテレータを更新
        } else {
            ++it;
        }
    }
    // ここでは it は有効な状態、またはend()に達している
    

const_iterator と iterator の混同

QMap::const_iterator は読み取り専用のイテレータであり、指している要素の値を変更することはできません。一方、QMap::iterator は値を変更できます。

エラー例

void printMap(const QMap<QString, int>& map) {
    QMap<QString, int>::iterator it; // const なマップに対して非 const イテレータを使おうとしている
    for (it = map.begin(); it != map.end(); ++it) { // コンパイルエラー
        qDebug() << it.key() << ":" << it.value();
    }
}

トラブルシューティング

  • 値を変更しない場合は const_iterator を推奨
    読み取り専用のループでは、const_iterator を使用することで、意図しない値の変更を防ぎ、コードの意図を明確にできます。パフォーマンス面でも有利になる場合があります。

  • const オブジェクトには const_iterator を使う
    constQMap オブジェクトに対して反復処理を行う場合、QMap::const_iterator を使用し、constBegin()constEnd() メソッドを使用します。

    void printMap(const QMap<QString, int>& map) {
        QMap<QString, int>::const_iterator it; // const_iterator を使用
        for (it = map.constBegin(); it != map.constEnd(); ++it) {
            qDebug() << it.key() << ":" << it.value();
        }
    }
    

end() イテレータのデリファレンス

QMap::end() が返すイテレータは、マップの最後の要素の「次」を指しており、有効な要素を指していません。このイテレータをデリファレンスしようとすると、未定義の動作(通常はクラッシュ)が発生します。

エラー例

QMap<int, QString> myMap;
// myMap は空

QMap<int, QString>::iterator it = myMap.begin();
if (it == myMap.end()) {
    qDebug() << "マップは空です";
} else {
    qDebug() << it.key(); // マップが空の場合、it は end() なので、これはエラー
}

トラブルシューティング

  • find() の結果を確認する
    QMap::find() メソッドは、要素が見つからない場合に QMap::end() を返します。find() の戻り値が end() と等しいかどうかを常に確認してから、そのイテレータをデリファレンスしてください。

    QMap<int, QString>::iterator it = myMap.find(5);
    if (it != myMap.end()) {
        qDebug() << "キー5が見つかりました:" << it.value();
    } else {
        qDebug() << "キー5は見つかりませんでした";
    }
    
  • ループの条件を正しく設定する
    通常のイテレータループでは、it != myMap.end() の条件でループを継続し、end() に達した時点でループを終了します。

    for (QMap<int, QString>::iterator it = myMap.begin(); it != myMap.end(); ++it) {
        // ここでは it は常に有効な要素を指している
        qDebug() << it.key() << ":" << it.value();
    }
    

一時オブジェクトのイテレータ

関数が QMap オブジェクトを値で返し、その一時的な QMap オブジェクトのイテレータを取得しようとすると、一時オブジェクトがスコープ外に出て破棄された後にイテレータが無効になります。

エラー例

QMap<QString, int> createMap() {
    QMap<QString, int> tempMap;
    tempMap.insert("A", 1);
    tempMap.insert("B", 2);
    return tempMap;
}

// ... どこかで ...
QMap<QString, int>::iterator it = createMap().begin(); // createMap() の戻り値は一時オブジェクト
                                                      // この行の終わりに tempMap は破棄され、it は無効になる
qDebug() << it.key(); // クラッシュまたは未定義の動作

トラブルシューティング

  • C++11 の範囲ベースforループを使用する
    C++11以降の範囲ベースforループは、多くのイテレータ関連のエラーを避けるのに役立ちます。コンテナのコピーまたは参照を自動的に処理し、イテレータの管理を抽象化してくれます。

    QMap<QString, int> myMap;
    myMap.insert("X", 10);
    myMap.insert("Y", 20);
    
    for (auto it = myMap.begin(); it != myMap.end(); ++it) {
        qDebug() << it.key() << ":" << it.value();
    }
    
    // または、よりシンプルに
    for (const auto& pair : myMap) { // 読み取り専用の場合
        qDebug() << pair.key() << ":" << pair.value();
    }
    // または、値を変更する場合
    for (auto& pair : myMap) {
        pair.value() = pair.value() * 2;
    }
    
  • QMap をコピーするか、参照で受け取る
    一時オブジェクトのイテレータに依存するのではなく、明示的に QMap のコピーを作成するか、関数が QMap を参照で返すようにします。

    QMap<QString, int> myNewMap = createMap(); // マップをコピー
    QMap<QString, int>::iterator it = myNewMap.begin();
    qDebug() << it.key(); // これで安全
    

    または

    // createMap を変更して QMap を参照で返すようにする(設計による)
    // QMap<QString, int>& createMap() { ... } // しかし、これは普通ではない
    

QMap のコピーとイテレータ

QMap は暗黙的な共有(Implicit Sharing)を使用しており、コピーされる際にデータのディープコピーは行われません。しかし、QMap のインスタンスが変更された場合、そのコピーは独立したディープコピーを作成します(Copy-on-Write)。これにより、コピー元とコピー先のマップが異なるデータを持つようになり、イテレータの有効性にも影響を与える可能性があります。

エラー例

QMap<int, QString> originalMap;
originalMap.insert(1, "A");

QMap<int, QString>::iterator it = originalMap.begin();

QMap<int, QString> copiedMap = originalMap; // ディープコピーはまだ発生していない

copiedMap.insert(2, "B"); // ここで originalMap と copiedMap が分岐し、ディープコピーが発生

qDebug() << it.key(); // originalMap のイテレータは有効だが、copiedMap からは独立している
// 問題は、copiedMap の変更が originalMap に影響しないことを理解しておく必要がある点

トラブルシューティング

  • どのマップのイテレータなのか明確にする
    複数の QMap インスタンスが存在する場合、どのマップのイテレータを操作しているのかを常に明確にしましょう。
  • 暗黙的な共有の挙動を理解する
    QMap の暗黙的な共有のメカニズムを理解し、いつディープコピーが発生するかを意識してください。


QMap::Iterator は、QMap コンテナ内の要素(キーと値のペア)を走査(イテレート)し、必要に応じて値を変更するために使用されます。C++の標準ライブラリ(STL)のイテレータと非常によく似た動作をします。

以下の例では、QMap を作成し、さまざまなQMap::Iterator の使い方を示します。

準備
これらのコードを実行するには、Qtプロジェクト(例えば、Qt Console Application)を作成し、.pro ファイルに QT += core を追加してください。

例1: マップの要素を順番に読み取る (基本的な走査)

最も基本的な使用法で、マップ内のすべての要素を最初から最後まで順にたどって読み取ります。

#include <QCoreApplication>
#include <QMap>
#include <QDebug> // qDebug() を使用するために必要

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    // QMap を作成し、キーと値のペアを追加
    QMap<QString, int> scores;
    scores.insert("Alice", 95);
    scores.insert("Bob", 80);
    scores.insert("Charlie", 70);
    scores.insert("David", 100);

    qDebug() << "--- マップの要素(読み取り) ---";

    // QMap::Iterator を宣言
    QMap<QString, int>::iterator it;

    // begin() から end() までループ
    // QMap はキーでソートされるため、アルファベット順に出力される
    for (it = scores.begin(); it != scores.end(); ++it) {
        // it.key() でキーにアクセス
        // it.value() で値にアクセス
        qDebug() << "名前:" << it.key() << ", スコア:" << it.value();
    }

    return a.exec();
}

出力例

--- マップの要素(読み取り) ---
名前: "Alice" , スコア: 95
名前: "Bob" , スコア: 80
名前: "Charlie" , スコア: 70
名前: "David" , スコア: 100

解説

  • it.value(): 現在のイテレータが指す要素の値を返します。QMap::Iterator の場合、これは変更可能な参照を返します。
  • it.key(): 現在のイテレータが指す要素のキーを返します。
  • ++it: イテレータを次の要素に進めます。
  • scores.end(): マップの最後の要素の「次」を指すイテレータを返します。ループの終了条件として使われます。
  • scores.begin(): マップの最初の要素を指すイテレータを返します。

例2: マップの要素の値を変更する

QMap::Iterator を使用すると、イテレータが指す要素の値を直接変更できます。

#include <QCoreApplication>
#include <QMap>
#include <QDebug>

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    QMap<QString, int> productPrices;
    productPrices.insert("Apple", 120);
    productPrices.insert("Banana", 80);
    productPrices.insert("Orange", 150);

    qDebug() << "--- 変更前の価格 ---";
    for (QMap<QString, int>::iterator it = productPrices.begin(); it != productPrices.end(); ++it) {
        qDebug() << it.key() << ":" << it.value();
    }

    qDebug() << "\n--- 価格を更新 ---";
    QMap<QString, int>::iterator it = productPrices.find("Banana"); // "Banana" のイテレータを取得
    if (it != productPrices.end()) { // 見つかった場合
        it.value() = 90; // 値を90に更新
        qDebug() << "Banana の価格を 90 に更新しました。";
    }

    // 全ての価格を10%値上げする
    for (it = productPrices.begin(); it != productPrices.end(); ++it) {
        it.value() = it.value() * 1.1; // 10%値上げ
    }

    qDebug() << "\n--- 変更後の価格 ---";
    for (it = productPrices.begin(); it != productPrices.end(); ++it) {
        qDebug() << it.key() << ":" << it.value();
    }

    return a.exec();
}

出力例

--- 変更前の価格 ---
"Apple" : 120
"Banana" : 80
"Orange" : 150

--- 価格を更新 ---
Banana の価格を 90 に更新しました。

--- 変更後の価格 ---
"Apple" : 132
"Banana" : 99
"Orange" : 165

解説

  • it.value() = 90;: it.value() は参照を返すため、直接値を代入して変更できます。
  • productPrices.find("Banana"): 指定されたキーを持つ要素のイテレータを返します。見つからない場合は productPrices.end() を返します。

例3: 条件に基づいて要素を削除する

ループ中に QMap から要素を削除する場合、QMap::erase() メソッドの戻り値に注意する必要があります。erase() は削除された要素の次の要素を指すイテレータを返します。

#include <QCoreApplication>
#include <QMap>
#include <QDebug>

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    QMap<QString, int> inventory;
    inventory.insert("Laptop", 10);
    inventory.insert("Mouse", 50);
    inventory.insert("Keyboard", 30);
    inventory.insert("Monitor", 5);

    qDebug() << "--- 削除前 ---";
    for (QMap<QString, int>::iterator it = inventory.begin(); it != inventory.end(); ++it) {
        qDebug() << it.key() << ":" << it.value();
    }

    qDebug() << "\n--- 在庫が10以下のアイテムを削除 ---";
    QMap<QString, int>::iterator it = inventory.begin();
    while (it != inventory.end()) {
        if (it.value() <= 10) {
            // erase() は削除された要素の次のイテレータを返す
            // このイテレータで it を更新することで、ループが継続できる
            qDebug() << "削除:" << it.key();
            it = inventory.erase(it);
        } else {
            ++it; // 次の要素に進む
        }
    }

    qDebug() << "\n--- 削除後 ---";
    for (it = inventory.begin(); it != inventory.end(); ++it) {
        qDebug() << it.key() << ":" << it.value();
    }

    return a.exec();
}

出力例

--- 削除前 ---
"Keyboard" : 30
"Laptop" : 10
"Monitor" : 5
"Mouse" : 50

--- 在庫が10以下のアイテムを削除 ---
削除: "Laptop"
削除: "Monitor"

--- 削除後 ---
"Keyboard" : 30
"Mouse" : 50

解説

  • else { ++it; }: 要素を削除しなかった場合は、手動でイテレータを進める必要があります。
  • it = inventory.erase(it);: 要素を削除した後、erase() が返す新しいイテレータで it を更新することが重要です。これにより、削除によってイテレータが無効になるのを防ぎ、正しく次の要素に進むことができます。

例4: QMap::const_iterator の使用

値を変更する必要がない読み取り専用の走査では、QMap::const_iterator を使用することが推奨されます。これは意図しない変更を防ぎ、コードの可読性を向上させます。

#include <QCoreApplication>
#include <QMap>
#include <QDebug>

// const QMap を受け取り、その内容を出力する関数
void printImmutableMap(const QMap<QString, double>& data) {
    qDebug() << "--- 不変マップの要素 ---";
    // constBegin() と constEnd() を使用
    for (QMap<QString, double>::const_iterator it = data.constBegin(); it != data.constEnd(); ++it) {
        qDebug() << it.key() << " -> " << it.value();
        // it.value() = 1.0; // コンパイルエラー!const_iterator では値は変更できない
    }
}

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    QMap<QString, double> sensorData;
    sensorData.insert("TempC", 25.5);
    sensorData.insert("Humidity", 60.2);
    sensorData.insert("PressurekPa", 101.3);

    printImmutableMap(sensorData);

    return a.exec();
}

出力例

--- 不変マップの要素 ---
"Humidity"  ->  60.2
"PressurekPa"  ->  101.3
"TempC"  ->  25.5

解説

  • QMap<QString, double>::const_iterator: const_iterator 型を明示的に指定します。
  • data.constBegin()data.constEnd(): const QMap オブジェクトに対して使用されるメソッドで、const_iterator を返します。
  • const QMap<QString, double>& data: 関数が const 参照でマップを受け取るため、関数内ではマップを変更できません。

例5: C++11 範囲ベースforループ (推奨される現代的な方法)

C++11以降では、イテレータを直接操作する代わりに、範囲ベースforループを使用すると、より簡潔でエラーの少ないコードを書くことができます。Qtのコンテナはこれに対応しています。

#include <QCoreApplication>
#include <QMap>
#include <QDebug>

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    QMap<QString, QString> capitals;
    capitals.insert("Japan", "Tokyo");
    capitals.insert("USA", "Washington D.C.");
    capitals.insert("France", "Paris");

    qDebug() << "--- 範囲ベースforループで読み取り(const参照) ---";
    // 要素を読み取るだけで変更しない場合、const参照を使う
    for (const auto& pair : capitals) {
        qDebug() << "国:" << pair.key() << ", 首都:" << pair.value();
    }

    QMap<QString, int> scores;
    scores.insert("Math", 85);
    scores.insert("Physics", 78);
    scores.insert("Chemistry", 92);

    qDebug() << "\n--- 範囲ベースforループで変更(非const参照) ---";
    // 要素の値を変更する場合、非const参照を使う
    for (auto& pair : scores) {
        pair.value() += 5; // スコアを5点追加
        qDebug() << pair.key() << "の新しいスコア:" << pair.value();
    }

    return a.exec();
}

出力例

--- 範囲ベースforループで読み取り(const参照) ---
国: "France" , 首都: "Paris"
国: "Japan" , 首都: "Tokyo"
国: "USA" , 首都: "Washington D.C."

--- 範囲ベースforループで変更(非const参照) ---
Math の新しいスコア: 90
Physics の新しいスコア: 83
Chemistry の新しいスコア: 97
  • 範囲ベースforループは、特に読み取り専用の走査や、ループ中に要素を削除しない場合に、非常に便利で読みやすいコードを提供します。ループ中に要素を削除する場合は、依然としてSTLスタイルのイテレータとerase()の戻り値を使う必要があります。
  • for (auto& pair : scores): scores マップの各要素を pair という名前の非 const 参照として取得します。これにより、pair.value() を介して値を変更できます。
  • for (const auto& pair : capitals): capitals マップの各要素(キーと値のペア)を pair という名前の const 参照として取得します。これにより、要素の読み取りはできますが変更はできません。


QMap::Iterator の代替方法

C++11 範囲ベースforループ (Range-based for loop)

これは現代のC++で最も推奨されるイテレーション方法です。QMap::Iterator を直接扱う必要がなく、コードが非常に簡潔になります。

特徴

  • 読み取り専用と変更可能
    const auto& を使えば読み取り専用、auto& を使えば値を変更できます。
  • 安全性
    イテレータの無効化(dangling iterator)のような一般的なエラーを避けることができます(ただし、ループ中に要素を削除する場合は注意が必要です)。
  • 簡潔性
    begin()end() の呼び出し、++it のようなイテレータの操作が不要になります。

コード例

#include <QCoreApplication>
#include <QMap>
#include <QDebug>

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    QMap<QString, int> scores;
    scores.insert("Alice", 95);
    scores.insert("Bob", 80);
    scores.insert("Charlie", 70);

    qDebug() << "--- 範囲ベースforループで読み取り(const) ---";
    // 読み取り専用の場合
    for (const auto& pair : scores) {
        qDebug() << "名前:" << pair.key() << ", スコア:" << pair.value();
    }

    qDebug() << "\n--- 範囲ベースforループで変更 ---";
    // 値を変更する場合
    for (auto& pair : scores) {
        pair.value() += 5; // スコアを5点加算
        qDebug() << "名前:" << pair.key() << ", 新スコア:" << pair.value();
    }

    return a.exec();
}
// Qt 5.10 以降 & C++17 以降
#include <QCoreApplication>
#include <QMap>
#include <QDebug>

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    QMap<QString, int> data;
    data["Alpha"] = 1;
    data["Beta"] = 2;

    for (const auto& [key, value] : data.asKeyValueRange()) { // Qt 6.4以降で推奨される asKeyValueRange()
        qDebug() << "キー:" << key << ", 値:" << value;
    }
    // または古いQtバージョンや手動でイテレータを使う場合:
    // for (auto it = data.keyValueBegin(); it != data.keyValueEnd(); ++it) {
    //     qDebug() << "キー:" << it->first << ", 値:" << it->second;
    // }

    return a.exec();
}

Javaスタイルのイテレータ (QMapIterator と QMutableMapIterator)

Qt 4から導入されたJavaスタイルのイテレータは、STLスタイルのイテレータとは異なるAPIを提供します。より高レベルな抽象化がされており、使い方が直感的であると感じる人もいます。

特徴

  • イテレータの無効化に対する耐性
    STLスタイルよりもイテレータの無効化に対して強いですが、マップの構造が大幅に変更される場合はやはり注意が必要です。
  • 読み取り専用と変更可能
    QMapIterator は読み取り専用、QMutableMapIterator は変更可能です。
  • 高レベルAPI
    hasNext(), next(), key(), value() といったメソッドで操作します。

コード例

#include <QCoreApplication>
#include <QMap>
#include <QMapIterator> // QMapIterator を使用するために必要
#include <QMutableMapIterator> // QMutableMapIterator を使用するために必要
#include <QDebug>

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    QMap<int, QString> students;
    students.insert(101, "佐藤");
    students.insert(102, "田中");
    students.insert(103, "鈴木");

    qDebug() << "--- QMapIterator で読み取り ---";
    QMapIterator<int, QString> i(students);
    while (i.hasNext()) {
        i.next(); // 次の要素に進む
        qDebug() << "ID:" << i.key() << ", 名前:" << i.value();
    }

    qDebug() << "\n--- QMutableMapIterator で変更と削除 ---";
    QMutableMapIterator<int, QString> mi(students);
    while (mi.hasNext()) {
        mi.next();
        if (mi.key() == 102) {
            mi.setValue("山本"); // 値を変更
            qDebug() << "ID 102 の名前を山本に更新しました。";
        }
        if (mi.key() == 103) {
            mi.remove(); // 要素を削除
            qDebug() << "ID 103 を削除しました。";
        }
    }

    qDebug() << "\n--- 変更後のマップ ---";
    for (const auto& pair : students) { // 範囲ベースforループで最終確認
        qDebug() << "ID:" << pair.key() << ", 名前:" << pair.value();
    }

    return a.exec();
}

出力例

--- QMapIterator で読み取り ---
ID: 101 , 名前: "佐藤"
ID: 102 , 名前: "田中"
ID: 103 , 名前: "鈴木"

--- QMutableMapIterator で変更と削除 ---
ID 102 の名前を山本に更新しました。
ID 103 を削除しました。

--- 変更後のマップ ---
ID: 101 , 名前: "佐藤"
ID: 102 , 名前: "山本"

解説

  • mi.remove();: 現在の要素をマップから削除します。QMutableMapIterator は削除後に自動的に次の有効な位置に進みます。
  • mi.setValue("山本");: 現在の要素の値を変更します。
  • i.next(): 次の要素に進み、その要素のキーと値にアクセスできるようになります。
  • i.hasNext(): 次の要素が存在するかどうかを返します。
  • QMapIterator<Key, Value> i(map);: イテレータをマップで初期化します。

キーのリスト (keys()) と値への直接アクセス (operator[], value())

マップ全体の走査が必要なく、特定のキーやその値にアクセスしたい場合、またはすべてのキー/値のリストを取得して別の方法で処理したい場合に便利です。

特徴

  • パフォーマンスへの考慮
    keys()values() はリストをコピーして返すため、大きなマップではパフォーマンスに影響を与える可能性があります。ループ内でoperator[]を使うと、要素数に比例して検索コストがかかる場合があります。
  • 値リストの取得
    values() メソッドでマップの全値を QList として取得できます。
  • キーリストの取得
    keys() メソッドでマップの全キーを QList として取得できます。
  • 特定の要素アクセス
    operator[]value() を使ってキーで直接値にアクセスできます。

コード例

#include <QCoreApplication>
#include <QMap>
#include <QDebug>
#include <QList> // QList を使用するために必要

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    QMap<QString, int> studentAges;
    studentAges.insert("Alice", 20);
    studentAges.insert("Bob", 22);
    studentAges.insert("Charlie", 19);

    qDebug() << "--- キーで直接アクセス ---";
    // キーで値にアクセス (存在しない場合はデフォルトコンストラクタで作成される)
    qDebug() << "Aliceの年齢:" << studentAges["Alice"];
    // 存在しないキーにアクセスする場合、QMapは新しい要素を作成します。
    // 既存のキーの値を変更する場合にも使えます。
    studentAges["David"] = 21; // 新しい要素を追加
    studentAges["Bob"] = 23;   // 既存の要素の値を変更

    qDebug() << "\n--- value() で安全にアクセス(デフォルト値指定) ---";
    // key()で存在しないキーにアクセスしようとすると、QMap::value()はデフォルト値を返す
    qDebug() << "Charlieの年齢:" << studentAges.value("Charlie", 0); // 19
    qDebug() << "Eveの年齢 (存在しない):" << studentAges.value("Eve", 18); // 18 (デフォルト値)

    qDebug() << "\n--- keys() と values() でリストを取得 ---";
    QList<QString> names = studentAges.keys();
    QList<int> ages = studentAges.values();

    qDebug() << "名前のリスト:" << names;
    qDebug() << "年齢のリスト:" << ages;

    qDebug() << "\n--- keys() でループし、値を取得 ---";
    // 読み取り専用の走査で、キーと値の両方が必要な場合
    for (const QString& name : names) {
        qDebug() << name << "さんの年齢:" << studentAges[name];
    }

    return a.exec();
}

出力例

--- キーで直接アクセス ---
Aliceの年齢: 20

--- value() で安全にアクセス(デフォルト値指定) ---
Charlieの年齢: 19
Eveの年齢 (存在しない): 18

--- keys() と values() でリストを取得 ---
名前のリスト: ("Alice", "Bob", "Charlie", "David")
年齢のリスト: (20, 23, 19, 21)

--- keys() でループし、値を取得 ---
Alice さんの年齢: 20
Bob さんの年齢: 23
Charlie さんの年齢: 19
David さんの年齢: 21
  • values(): マップ内のすべての値を QList として返します。キーの順序に対応しています。
  • keys(): マップ内のすべてのキーを昇順にソートされた QList として返します。
  • value(const Key& key, const T& defaultValue = T()) const: マップからキーに関連付けられた値を返します。キーが存在しない場合は defaultValue を返します。これは読み取り専用の安全なアクセス方法です。
  • operator[]: マップへの書き込みアクセスを提供します。キーが存在しない場合は新しい要素が挿入されます。キーが存在する場合はそのキーに関連付けられた値の参照を返します。
  • すべてのキーまたは値のリストが必要な場合

    • keys() または values(): リストとして取得して別の処理を行う場合に便利です。ただし、内部でコピーが発生することに注意してください。
  • 特定のキーで値にアクセスする場合

    • operator[] または value(): 最も直接的な方法です。value() はキーが存在しない場合のデフォルト値の指定が安全です。
  • 読み取り専用で、古いQt/C++環境の場合

    • STLスタイルの const_iterator (QMap::const_iterator): C++11より前の環境や、パフォーマンスを重視する場合に有効です。
    • Javaスタイルの QMapIterator: より簡潔なAPIを好む場合に有効です。
  • 要素の削除が必要な場合

    • STLスタイルのイテレータ (QMap::IteratorQMap::erase()): ループ中に要素を削除する必要がある場合、erase() の戻り値でイテレータを更新しながらループを進めるのが最も一般的な方法です。
    • Javaスタイルのミュータブルイテレータ (QMutableMapIterator::remove()): 削除が必要な場合のもう一つの選択肢です。STLスタイルよりも直感的に感じられるかもしれません。
  • 最も推奨される方法 (現代C++)

    • C++11 範囲ベースforループ (for (auto& pair : myMap)): ほとんどの読み取りと一部の変更操作において、最も簡潔で安全な選択肢です。ループ中に要素を削除しない場合に特に適しています。Qt 5.10以降ではkeyValueBegin/keyValueEnd、Qt 6.4以降ではasKeyValueRange()を使うことで、キーと値のペアを直接扱うことができ、より強力です。