本文へスキップします。

本文へ

【全Qt】
【全・Qt】SRAロゴ
H1

Qt 6 のコンテナクラスをどう選ぶか

技術記事

Qt 6 のコンテナクラスをどう選ぶか

(掲載 2026年8月26日)

Qt コンテナクラス

Qt でコレクションを扱うとき、順番を保ちたいのか、ID から値を探したいのか、キー順に表示したいのかによって、適切なコンテナは変わります。 本記事では Qt 6.11 を対象に、QListQHashQMap の選定基準と安全な使い方を整理します。

コード例は C++20 を前提にしています。なお、Qt 6 の QVectorQList の別名です。 両者を性能やメモリ配置の異なるコンテナとして比較する必要はありません。

この記事では、Qt 公式ドキュメントの Container Classes をもとに、用途別の選定基準、基本操作、注意点を整理します。


まずは選定早見表

要件 推奨 キー要件 走査順 主な注意点
入力順を保った要素列、添字アクセス QList<T> なし 挿入順 中間への挿入・削除は要素移動を伴い得る
ID から値を頻繁に検索する QHash<Key, T> operator==qHash() 任意。依存しない 表示順には使えない
キー順に表示・範囲検索する QMap<Key, T> 全順序を定める operator< キーの昇順 順序を維持するコストがある

「速いコンテナ」を選ぶのではなく、アクセスの仕方を先に決めます。順番をそのまま持ちたいなら QList、キーから一点を取得したいなら QHash、 ソート済みの一覧や範囲検索が必要なら QMap、という順に考えると判断しやすくなります。

QList:順番のある値の列

QList<T> は要素を連続したメモリに保持する動的配列です。添字によるアクセス、末尾への追加、順番どおりの反復に向きます。 事前におおよその件数が分かるなら、reserve() で容量を予約すると再確保の回数を抑えられます。

#include <QList>
#include <QString>

QList<QString> recentFiles()
{
    QList<QString> files;
    files.reserve(3);
    files.append(QStringLiteral("report.pdf"));
    files.append(QStringLiteral("summary.xlsx"));
    files.append(QStringLiteral("diagram.svg"));
    return files;
}

途中への挿入・削除が頻繁なら、後続要素の移動のコストを踏まえて設計します。また、QList は暗黙共有の値型です。 共有後に変更すると分離が発生する場合があり、変更や分離でイテレータが無効になることがあります。

QHash:順序を要求しない ID 検索

QHash<Key, T> はキーと値を結び付けるハッシュ表です。商品 ID、設定名、キャッシュキーのように、キーから一点を取得することが主目的なら有力です。 独自のキー型では、等価比較の operator== と、Qt 6 ではシードを受け取る 2 引数の qHash() を実装します。

#include <QHash>
#include <QString>

int quantityFor(const QHash<QString, int> &stock,
                const QString &productId)
{
    return stock.value(productId, 0);
}

検索だけが目的なら、非 const の operator[] は使わない方が安全です。stock[productId] はキーが存在しないとき、 既定構築した値を持つ要素を追加します。value() は追加せずに値を取得できます。

順序に関する注意:QHash の反復順は任意です。表示順、シリアライズ順、テストの期待値には利用せず、 順序が必要なら QMap または明示的なソートを選びます。

QMap:キー順と範囲検索を意味に持たせる

QMap<Key, T> はキー順を維持する連想コンテナです。商品コード順の帳票、日付順のイベント、指定キー以上の項目を扱う範囲検索に向きます。 キー型は operator< により全順序を定める必要があり、反復ではキーの昇順で項目を取得できます。

#include <QMap>
#include <QString>

QMap<QString, int> reorderLevels()
{
    QMap<QString, int> levels;
    levels.insert(QStringLiteral("A-100"), 20);
    levels.insert(QStringLiteral("B-200"), 10);
    levels.insert(QStringLiteral("C-300"), 15);
    return levels;
}

この戻り値を cbegin() から cend() まで反復すれば、商品コードの昇順で処理されます。QMap の非 const operator[] も 存在しないキーを挿入するため、読み取りには value()、有無の判定には contains() を使い分けます。

標準 C++ コンテナとの使い分け

Qt の UI、シグナル・スロット、Qt 型との相互運用が中心なら、Qt コンテナは周辺 API と揃えやすい選択肢です。 QListstd::vectorQHashstd::unordered_mapQMapstd::map は、必要な操作と公開 API の境界で比較します。 標準コンテナを公開する既存ライブラリでは、無理に Qt コンテナへ寄せない方が単純なことがあります。

実践例:商品 ID で在庫を引き、帳票ではコード順に並べる

在庫の更新・検索には QHash、不足品の帳票には QMap、入荷の記録順には QList を使います。 並び順という要件を、戻り値の型で表している点が重要です。

#include <QHash>
#include <QList>
#include <QMap>
#include <QString>

struct Item {
    QString name;
    int quantity = 0;
};

class Inventory
{
public:
    void receive(const QString &id, const QString &name, int received)
    {
        auto it = items_.find(id);
        if (it == items_.end())
            it = items_.insert(id, Item{name, 0});
        it.value().quantity += received;
        receivedIds_.append(id);
    }

    QMap<QString, int> shortages(int threshold) const
    {
        QMap<QString, int> result;
        for (auto it = items_.cbegin(); it != items_.cend(); ++it) {
            if (it.value().quantity < threshold)
                result.insert(it.key(), it.value().quantity);
        }
        return result;
    }

private:
    QHash<QString, Item> items_;
    QList<QString> receivedIds_;
};

よくある落とし穴

  1. 検索のつもりで非 const の operator[] を使う。 QHashQMap では要素追加が起きるため、value() または contains()value() を使います。
  2. QHash の順序を表示順にする。 順序は任意です。キー順が必要なら QMap、別の順序なら明示的なソートを選びます。
  3. 反復中の変更と暗黙共有を軽視する。 とくに QList の変更ではイテレータが無効になり得ます。反復子を使う範囲では、コピー・挿入・削除の影響を API ごとに確認します。

まとめ

  • 順番のある要素列と添字アクセスには QList を選ぶ。Qt 6 の QVector は別名です。
  • キーからの平均的に高速な検索には QHash を選び、順序には依存しません。
  • キー順の表示や範囲検索には QMap を選び、キーの operator< を設計します。
  • 非 const の operator[] は更新用であり、読み取りには value() を優先します。

参考資料