QMapのイテレーション完全ガイド:end()の役割とモダンな代替アプローチ

2025-05-27

QMapとは

まず QMap について簡単に説明します。QMap<Key, T> は、キーと値のペアを格納するQtのテンプレートコンテナクラスです。キーは一意であり、QMap内の要素はキーによってソートされます。Pythonの辞書やC++のstd::mapのようなものと考えるとわかりやすいでしょう。

QMap::end() は、QMap最後の要素の次を指すイテレータを返します。これは、実際の要素を指すわけではありません。end() が返すイテレータは、コンテナの終わりを示すマーカーとして機能します。

一般的なループ処理で QMap の全要素を走査する際に、ループの終了条件としてよく使われます。


#include <QMap>
#include <QDebug>

int main() {
    QMap<QString, int> scores;
    scores.insert("Alice", 90);
    scores.insert("Bob", 85);
    scores.insert("Charlie", 95);

    // QMapの要素をイテレートする
    QMap<QString, int>::iterator i;
    for (i = scores.begin(); i != scores.end(); ++i) {
        qDebug() << "Key:" << i.key() << ", Value:" << i.value();
    }

    // 特定のキーを探す例
    QMap<QString, int>::iterator it = scores.find("Bob");
    if (it != scores.end()) { // end() と比較して要素が見つかったかを確認
        qDebug() << "Bob's score is:" << it.value();
    } else {
        qDebug() << "Bob not found.";
    }

    return 0;
}
  • iterator と const_iterator
    • QMap::end() は、値を変更可能な QMap::iterator を返します。
    • QMap::constEnd() は、値を変更できない QMap::const_iterator を返します。読み取り専用の処理を行う場合は constEnd() を使用することが推奨されます。
  • QMap::find() との連携
    QMap::find(key) 関数は、指定されたキーが見つかった場合、そのキーを指すイテレータを返します。見つからなかった場合は QMap::end() を返します。これを利用して、キーが存在するかどうかを効率的にチェックできます。
  • QMap::begin() との比較
    QMap が空の場合、QMap::begin()QMap::end() と同じイテレータを返します。これによって、コンテナが空かどうかを簡単に判断できます。
  • ループの終了条件
    for ループなどで QMap の要素を順に処理する際、i != scores.end() という条件でループを継続させます。イテレータ iend() と等しくなると、すべての要素を処理し終えたことになります。
  • 「最後の要素の次」を指す
    end() が返すイテレータは、コンテナの有効な要素を指しているわけではありません。そのため、end() が指すイテレータに対して * 演算子(デリファレンス)を使用すると、未定義の動作(クラッシュなど)を引き起こす可能性があります。


よくある間違い (Common Errors)

    • 間違い
      QMap::end() が指すイテレータは、有効な要素を指しているわけではありません。最後の要素の「次」を指す番兵イテレータです。これに対して * 演算子や key()value() などを呼び出すと、未定義の動作 (Undefined Behavior) となり、アプリケーションがクラッシュする原因となります。
      QMap<QString, int> myMap;
      // ... (要素を追加)
      
      QMap<QString, int>::iterator it = myMap.end();
      // 以下の行はクラッシュの原因となる可能性が高い
      qDebug() << it.key(); // 間違い!
      
    • 解決策
      必ず begin() から end() の範囲内でイテレートし、end() に到達する前にループを終了するようにします。find() で要素が見つからなかった場合も同様に end() と比較し、デリファレンスしないようにします。
  1. begin()end() が異なる一時オブジェクトを指す (Iterators from different temporary objects)

    • 間違い
      関数から QMap を返す際、その戻り値を一時オブジェクトとして受け取り、begin()end() をそれぞれ異なるタイミングで呼び出すと、それぞれのイテレータが異なる一時オブジェクトを指してしまい、比較が常に false となり、無限ループやクラッシュの原因になります。
      QMap<QString, int> getMap() {
          QMap<QString, int> tempMap;
          tempMap.insert("X", 10);
          return tempMap;
      }
      
      // 間違いの例
      for (auto it = getMap().begin(); it != getMap().end(); ++it) {
          // getMap().begin() と getMap().end() はそれぞれ異なる一時オブジェクトを生成する
          // そのため、it が永遠に getMap().end() と等しくならない可能性がある
          qDebug() << it.key();
      }
      
    • 解決策
      QMap のインスタンスを明示的に変数に格納してからイテレーションを行います。
      QMap<QString, int> getMap() {
          QMap<QString, int> tempMap;
          tempMap.insert("X", 10);
          return tempMap;
      }
      
      // 正しい例
      QMap<QString, int> myMap = getMap(); // 一時オブジェクトをコピーまたは移動で受け取る
      for (auto it = myMap.begin(); it != myMap.end(); ++it) {
          qDebug() << it.key();
      }
      
      // C++11の範囲ベースforループの利用も有効 (QMapのキー/値ペアに直接アクセスする場合)
      // ただし、QtのQMapはそのままではキーにアクセスできない点に注意 (QMap::key_value_iteratorを使う)
      for (auto value : myMap) { // 値のみをイテレート
          qDebug() << value;
      }
      
      Qt 5.10以降の QMap::keyValueBegin()QMap::keyValueEnd() を使用するか、Q_FOREACH (Qt 6では非推奨) や C++17の構造化バインディング (for (auto [key, value] : map)) を使用することで、より簡潔に記述できます。
  2. const_iteratoriterator の混在 (Mixing const_iterator and iterator)

    • 間違い
      const QMap オブジェクトに対して begin()end() を呼び出すと const_iterator が返されますが、非 constQMap に対して constBegin()constEnd() を呼び出すこともできます。これらを誤って組み合わせると、型不一致のエラーや、コンテナのコピーオンライト動作を不必要にトリガーする可能性があります。
      QMap<QString, int> myMap;
      // ...
      
      QMap<QString, int>::const_iterator it = myMap.begin(); // const_iterator に代入してもOKだが、
      // myMap.begin() は非const版を返すため、myMapが暗黙的にデタッチされる可能性がある
      
    • 解決策
      • コンテナの内容を変更しない場合は、常に const_iteratorconstBegin() / constEnd() を使用します。これにより、コンテナが不必要にデタッチされるのを防ぎ、パフォーマンスを向上させることができます。
      • C++11以降では auto キーワードを使用して、適切なイテレータ型を自動的に推論させるのがベストプラクティスです。qAsConst() ヘルパー関数と組み合わせることで、非 const なコンテナを const として扱い、const_iterator を取得できます。
      QMap<QString, int> myMap;
      // ...
      
      for (auto it = qAsConst(myMap).constBegin(); it != qAsConst(myMap).constEnd(); ++it) {
          // myMapの内容は変更されない
          qDebug() << it.key();
      }
      
      // C++17以降の範囲ベースforループと構造化バインディング (QMap::keyValueBegin/Endを使用)
      for (auto const& [key, value] : qAsConst(myMap).asKeyValueRange()) {
          qDebug() << key << value;
      }
      

QMap::end() 関連のエラーに遭遇した場合、以下の点を確認してみてください。

  1. スタックトレースの確認
    クラッシュが発生した場合、デバッガでスタックトレースを確認します。イテレータのデリファレンスが原因であれば、QMap::iterator::key()QMap::iterator::value() のような関数がスタックトップに近い位置にあることが多いです。
  2. イテレータの有効性の確認
    ループの各ステップでイテレータが end() と等しくないか (it != map.end()) を常に確認しているか確認します。
  3. コンテナの寿命 (Lifetime of the container)
    イテレータが指す QMap オブジェクトが、イテレータが使用されている間も有効であることを確認します。例えば、関数から一時的に返された QMap のイテレータを保持し続けると、その QMap がスコープを抜けて破棄されたときにイテレータが無効になります。
  4. operator[] の使用注意
    QMap::operator[](const Key &key) は、キーが存在しない場合にデフォルトコンストラクトされた値で新しい要素を挿入します。意図しない挿入を防ぎたい場合は、QMap::contains(key) で存在を確認するか、QMap::value(key, defaultValue) を使用することを検討してください。これは end() と直接関係ありませんが、QMap を扱う上でよくある落とし穴です。
  5. QMap の要素型 (Element types of QMap)
    QMap のキー型には operator<() が、値型には代入可能 (assignable) であることが求められます。これらが満たされていない場合、コンパイルエラーや予期せぬ動作につながることがあります。特にカスタム型をキーにする場合は注意が必要です。


基本的なイテレーション (Basic Iteration)

QMap のすべての要素を最初から最後まで順に処理する最も一般的な方法です。

#include <QMap>
#include <QDebug>

int main() {
    QMap<QString, int> studentScores;
    studentScores.insert("Alice", 90);
    studentScores.insert("Bob", 75);
    studentScores.insert("Charlie", 88);
    studentScores.insert("David", 92);

    qDebug() << "--- All Student Scores ---";

    // QMap::begin() から QMap::end() までイテレートする
    // end() は「最後の要素の次」を指すため、ループ条件は `it != studentScores.end()` となる
    for (QMap<QString, int>::iterator it = studentScores.begin(); it != studentScores.end(); ++it) {
        qDebug() << "Name:" << it.key() << ", Score:" << it.value();
    }

    return 0;
}

出力例

--- All Student Scores ---
Name: "Alice" , Score: 90
Name: "Bob" , Score: 75
Name: "Charlie" , Score: 88
Name: "David" , Score: 92

解説

  • ループ条件 it != studentScores.end() は、イテレータ itend() に到達するまでループを継続することを保証します。itend() になると、すべての要素が処理されたことになります。
  • studentScores.end()QMap の最後の要素の次を指すイテレータを返します。
  • studentScores.begin()QMap の最初の要素を指すイテレータを返します。

特定のキーの検索と end() の利用 (Searching for a Key and Using end())

QMap::find() 関数は、指定されたキーが見つかった場合、そのキーを指すイテレータを返します。見つからなかった場合は QMap::end() を返します。これを利用して、キーが存在するかどうかを効率的にチェックできます。

#include <QMap>
#include <QDebug>

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

    // "Japan" を探す
    QMap<QString, QString>::iterator itJapan = capitals.find("Japan");
    if (itJapan != capitals.end()) { // end() と比較して見つかったか確認
        qDebug() << "Capital of Japan is:" << itJapan.value();
    } else {
        qDebug() << "Japan not found in the map.";
    }

    // "Germany" を探す (存在しないキー)
    QMap<QString, QString>::iterator itGermany = capitals.find("Germany");
    if (itGermany != capitals.end()) {
        qDebug() << "Capital of Germany is:" << itGermany.value();
    } else {
        qDebug() << "Germany not found in the map."; // こちらが実行される
    }

    return 0;
}

出力例

Capital of Japan is: "Tokyo"
Germany not found in the map.

解説

  • capitals.find("Germany") は "Germany" キーを見つけられないので、itGermanycapitals.end() と同じイテレータを返します。したがって itGermany != capitals.end()false となり、"Germany not found" のメッセージが出力されます。
  • capitals.find("Japan") は "Japan" キーを見つけるので、itJapan は有効な要素を指します。したがって itJapan != capitals.end()true となり、その値が出力されます。

QMap が空かどうかをチェックする (Checking if QMap is Empty)

QMap が空の場合、begin()end() と同じイテレータを返します。これを利用して、コンテナが空かどうかを簡単に判断できます。

#include <QMap>
#include <QDebug>

int main() {
    QMap<int, QString> emptyMap;
    QMap<int, QString> nonEmptyMap;
    nonEmptyMap.insert(1, "One");

    qDebug() << "--- Checking Empty Maps ---";

    if (emptyMap.begin() == emptyMap.end()) {
        qDebug() << "emptyMap is indeed empty.";
    } else {
        qDebug() << "emptyMap is NOT empty.";
    }

    if (nonEmptyMap.begin() == nonEmptyMap.end()) {
        qDebug() << "nonEmptyMap is empty.";
    } else {
        qDebug() << "nonEmptyMap is NOT empty."; // こちらが実行される
    }

    // より一般的な方法は isEmpty() を使うことですが、end() の性質を示す例として
    if (emptyMap.isEmpty()) {
        qDebug() << "emptyMap.isEmpty() also confirms it's empty.";
    }

    return 0;
}

出力例

--- Checking Empty Maps ---
emptyMap is indeed empty.
nonEmptyMap is NOT empty.
emptyMap.isEmpty() also confirms it's empty.

解説

  • nonEmptyMap は要素があるため、nonEmptyMap.begin()nonEmptyMap.end() は異なる位置を指します。
  • emptyMap は要素がないため、emptyMap.begin()emptyMap.end() は同じ位置を指します。

範囲ベースforループと end() (Range-Based for Loop and end())

C++11以降で導入された範囲ベースforループは、コンテナのイテレーションをより簡潔に記述できます。内部的には begin()end() を利用しています。

#include <QMap>
#include <QDebug>

int main() {
    QMap<QString, double> itemPrices;
    itemPrices.insert("Apple", 1.50);
    itemPrices.insert("Banana", 0.75);
    itemPrices.insert("Orange", 1.20);

    qDebug() << "--- Item Prices (Range-Based For Loop) ---";

    // QMapはキーと値のペアを直接イテレートできないため、
    // Qt 5.10以降のkeyValueBegin()/keyValueEnd()を使うか、
    // Qt 6.4以降のasKeyValueRange()を使うのが一般的です。
    // ここでは、値のみをイテレートする例 (QMapはQListのようには直接ペアを返しません)

    // Qt 5.10以降推奨: キーと値を同時に取得する
    for (auto it = itemPrices.keyValueBegin(); it != itemPrices.keyValueEnd(); ++it) {
        qDebug() << "Item:" << it->first << ", Price:" << it->second;
    }

    qDebug() << "--- Item Prices (Qt 6.4+ using asKeyValueRange) ---";
    // Qt 6.4以降推奨: 構造化バインディングと asKeyValueRange()
    for (auto const& [key, value] : itemPrices.asKeyValueRange()) {
        qDebug() << "Item:" << key << ", Price:" << value;
    }

    return 0;
}

出力例

--- Item Prices (Range-Based For Loop) ---
Item: "Apple" , Price: 1.5
Item: "Banana" , Price: 0.75
Item: "Orange" , Price: 1.2
--- Item Prices (Qt 6.4+ using asKeyValueRange) ---
Item: "Apple" , Price: 1.5
Item: "Banana" , Price: 0.75
Item: "Orange" , Price: 1.2

解説

  • Qt 6.4以降では、QMap::asKeyValueRange() と C++17の構造化バインディングを組み合わせることで、最も簡潔にキーと値のペアをイテレートできます。これらも内部的には begin()end() の概念に基づいています。
  • Qt 5.10以降では、QMap::keyValueBegin()QMap::keyValueEnd() を使用することで、std::pair のようなオブジェクトを返すイテレータを取得できます。
  • QMap は、C++標準ライブラリの std::map とは異なり、直接 std::pair<Key, Value> を返すイテレータを提供していません。

QMap の内容を変更するつもりがなく、読み取り専用でアクセスしたい場合は、const_iteratorconstEnd() を使用するのが良いプラクティスです。これはパフォーマンスの向上(コピーオンライトの抑制)にもつながります。

#include <QMap>
#include <QDebug>

// QMapをconst参照で受け取る関数
void printMapContents(const QMap<int, QString>& dataMap) {
    qDebug() << "--- Printing Map Contents (const) ---";
    // const QMapなので、constBegin() と constEnd() を使用
    for (QMap<int, QString>::const_iterator it = dataMap.constBegin(); it != dataMap.constEnd(); ++it) {
        qDebug() << "Key:" << it.key() << ", Value:" << it.value();
    }
}

int main() {
    QMap<int, QString> myData;
    myData.insert(10, "Ten");
    myData.insert(20, "Twenty");
    myData.insert(30, "Thirty");

    printMapContents(myData);

    // main関数内で const_iterator を使用する例
    qDebug() << "--- Printing Map Contents (const in main) ---";
    for (QMap<int, QString>::const_iterator it = myData.constBegin(); it != myData.constEnd(); ++it) {
        // it.value() = "New Value"; // コンパイルエラー: const_iterator なので変更不可
        qDebug() << "Key:" << it.key() << ", Value:" << it.value();
    }

    return 0;
}

出力例

--- Printing Map Contents (const) ---
Key: 10 , Value: "Ten"
Key: 20 , Value: "Twenty"
Key: 30 , Value: "Thirty"
--- Printing Map Contents (const in main) ---
Key: 10 , Value: "Ten"
Key: 20 , Value: "Twenty"
Key: 30 , Value: "Thirty"
  • const_iterator は指し示す要素の値を変更することを許可しません。
  • printMapContents 関数は const QMap<int, QString>& を引数として受け取るため、その中では constBegin()constEnd() のみが使用可能です。


範囲ベースforループ (Range-Based for Loop) - C++11以降

C++11で導入された範囲ベースforループは、begin()end() を内部的に使用してコンテナのイテレーションを簡潔に記述できる、現代的なC++の機能です。QMap を直接イテレートする際に、キーと値を同時に取得する方法がQtのバージョンによって進化しています。

Qt 5.10 から Qt 6.3 まで: QMap::keyValueBegin() / QMap::keyValueEnd()

これらの関数は、キーと値のペアを std::pair のようにアクセスできるイテレータを返します。

#include <QMap>
#include <QDebug>

int main() {
    QMap<QString, int> dataMap;
    dataMap.insert("One", 1);
    dataMap.insert("Two", 2);
    dataMap.insert("Three", 3);

    qDebug() << "--- Using keyValueBegin/End (Qt 5.10+) ---";
    for (auto it = dataMap.keyValueBegin(); it != dataMap.keyValueEnd(); ++it) {
        qDebug() << "Key:" << it->first << ", Value:" << it->second;
    }

    return 0;
}

Qt 6.4 以降: QMap::asKeyValueRange() と構造化バインディング (Structured Bindings) - C++17以降

Qt 6.4で導入された asKeyValueRange() 関数は、より簡潔な構文を提供し、C++17の構造化バインディングと組み合わせることで非常に読みやすくなります。

#include <QMap>
#include <QDebug>

int main() {
    QMap<QString, int> dataMap;
    dataMap.insert("Apple", 50);
    dataMap.insert("Banana", 30);
    dataMap.insert("Cherry", 80);

    qDebug() << "--- Using asKeyValueRange (Qt 6.4+) ---";
    for (auto const& [key, value] : dataMap.asKeyValueRange()) {
        qDebug() << "Fruit:" << key << ", Quantity:" << value;
    }

    return 0;
}

利点

  • 安全性
    イテレータの範囲外アクセスなどのミスを防ぎやすくなります。
  • 簡潔性
    ループの記述が短くなり、読みやすくなります。

注意点

  • コンテナを読み取り専用でイテレートする場合でも、QMap が非 const の場合は、暗黙的な共有(Copy-on-Write)がトリガーされ、コンテナがデタッチされる可能性があります。これを防ぐには、qAsConst(dataMap).asKeyValueRange() のように qAsConst を使用することを検討してください。

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

Qtは、Javaのイテレータパターンに似た独自のイテレータクラスを提供しています。これらは、STLスタイルイテレータよりも高レベルで使いやすいとされていますが、一般的にSTLスタイルイテレータよりもわずかに効率が悪いとされています。イテレーション中に要素の削除を安全に行いたい場合に特に便利です。

QMapIterator (読み取り専用)

#include <QMap>
#include <QMapIterator>
#include <QDebug>

int main() {
    QMap<int, QString> ages;
    ages.insert(25, "Alice");
    ages.insert(30, "Bob");
    ages.insert(22, "Charlie");

    qDebug() << "--- Using QMapIterator ---";
    QMapIterator<int, QString> i(ages);
    while (i.hasNext()) {
        i.next(); // 次の要素に進む
        qDebug() << "Age:" << i.key() << ", Name:" << i.value();
    }

    return 0;
}

QMutableMapIterator (読み書き可能、要素の削除が可能)

イテレーション中に要素を安全に削除したい場合に非常に役立ちます。

#include <QMap>
#include <QMutableMapIterator>
#include <QDebug>

int main() {
    QMap<QString, int> stock;
    stock.insert("Milk", 5);
    stock.insert("Bread", 1);
    stock.insert("Eggs", 12);
    stock.insert("Butter", 0); // 在庫切れ

    qDebug() << "--- Before Removal ---" << stock;

    QMutableMapIterator<QString, int> i(stock);
    while (i.hasNext()) {
        i.next();
        if (i.value() == 0) {
            i.remove(); // 現在の要素を安全に削除し、イテレータは次の有効な位置に自動的に移動
        }
    }

    qDebug() << "--- After Removal (Zero Stock Items) ---" << stock;

    return 0;
}

利点

  • 安全な削除
    QMutableMapIterator::remove() を使用することで、イテレーション中に要素を削除してもイテレータが無効になる心配がありません。
  • 使いやすさ
    hasNext(), next(), key(), value() といった直感的なメソッドが提供されます。

注意点

  • Q_FOREACH と同様に、コンテナのコピーが発生する可能性があります(ただし、Qtの暗黙的な共有により通常は高速です)。
  • パフォーマンスがSTLスタイルイテレータよりわずかに劣る可能性があります。

QMap::keys() と QMap::values() の利用

QMap のすべてのキーまたはすべての値を QList として取得し、その QList をイテレートする方法です。

キーのリストをイテレート

#include <QMap>
#include <QList>
#include <QDebug>

int main() {
    QMap<QString, QString> dictionary;
    dictionary.insert("hello", "こんにちは");
    dictionary.insert("world", "世界");
    dictionary.insert("goodbye", "さようなら");

    qDebug() << "--- Iterating Keys and Looking up Values ---";
    QList<QString> allKeys = dictionary.keys(); // 全キーをQListとして取得
    for (const QString& key : allKeys) {
        qDebug() << "Key:" << key << ", Value:" << dictionary.value(key); // value()で値を取得
    }

    return 0;
}

値のリストをイテレート

#include <QMap>
#include <QList>
#include <QDebug>

int main() {
    QMap<int, QString> productNames;
    productNames.insert(101, "Laptop");
    productNames.102, "Mouse");
    productNames.insert(103, "Keyboard");

    qDebug() << "--- Iterating Values ---";
    QList<QString> allValues = productNames.values(); // 全値をQListとして取得
    for (const QString& value : allValues) {
        qDebug() << "Product Name:" << value;
    }

    return 0;
}

利点

  • キーのみ、または値のみが必要な場合に便利です。
  • 非常にシンプルで読みやすいコードになります。

注意点

  • イテレーション中に元の QMap を変更しても、コピーされた QList は影響を受けません。
  • keys()values() を呼び出すたびに、QMap の内容が QList にコピーされるため、大規模なマップや頻繁なイテレーションではパフォーマンスのオーバーヘッドが生じる可能性があります。

Qt 4の時代に導入された Q_FOREACH マクロ(または単に foreach)は、C++11の範囲ベースforループに似た構文を提供しましたが、現在は非推奨です。新しいコードでは使用を避けるべきです。

#include <QMap>
#include <QDebug>

// QT_NO_FOREACH が定義されていない場合にのみコンパイルされます
// 新しいコードでは使用を避けるべきです
#ifndef QT_NO_FOREACH
#define QT_NO_FOREACH // この行をコメントアウトすると Q_FOREACH の使用例として機能します

int main() {
    QMap<QString, int> scores;
    scores.insert("Anna", 85);
    scores.insert("Ben", 92);

    qDebug() << "--- Using Q_FOREACH (Deprecated) ---";
    // QMapの場合、Q_FOREACH は自動的に値にアクセスします
    // キーと値の両方が必要な場合は、map.keys() を介してキーを取得し、map.value(key) で値を取得します
    Q_FOREACH (const QString& key, scores.keys()) {
        qDebug() << "Student:" << key << ", Score:" << scores.value(key);
    }

    return 0;
}
#endif

注意点

  • C++11の範囲ベースforループの登場により、その必要性が薄れ、Qt 5.7以降では非推奨となりました。
  • Q_FOREACH はコンテナのコピーを作成します。

現代のQtプログラミングでは、ほとんどのイテレーションシナリオで以下の方法が推奨されます。

  • 特定の要素を探す場合

    • QMap::find()QMap::end() を使用したSTLスタイルイテレータ
  • キーのみ/値のみをイテレートする場合

    • QMap::keys() / QMap::values() を使用し、結果の QList を範囲ベースforループでイテレート
    • Qt 6.4以降: QMap::asKeyValueRange() と構造化バインディング (for (auto const& [key, value] : map.asKeyValueRange()))
    • Qt 5.10以降: QMap::keyValueBegin() / QMap::keyValueEnd() (for (auto it = map.keyValueBegin(); it != map.keyValueEnd(); ++it))
    • コンテナの内容を変更する場合
      QMutableMapIterator