[C++] STLのコンテナについてわかりやすく詳しく解説
C++のSTL(Standard Template Library)には、データ構造を効率的に扱うためのコンテナが用意されています。
主なコンテナには、配列型のvector
やarray
、双方向リストのlist
、双方向連結リストのdeque
、集合型のset
やunordered_set
、マップ型のmap
やunordered_map
、スタックやキューのstack
、queue
、priority_queue
があります。
それぞれのコンテナは、データの挿入、削除、検索、並べ替えなどに特化した機能を持ち、用途に応じて選択します。
例えば、vector
は動的配列でランダムアクセスが高速、map
はキーと値のペアを管理し、set
は重複を許さない集合を扱います。
STLコンテナとは何か
STL(Standard Template Library)は、C++の標準ライブラリの一部であり、データ構造やアルゴリズムを効率的に利用するためのテンプレートクラスの集合です。
STLのコンテナは、データを格納するための構造を提供し、プログラマがデータを管理する際の負担を軽減します。
これにより、開発者はデータの格納、検索、操作を簡単に行うことができます。
STLコンテナは主に以下の3つのカテゴリに分けられます。
カテゴリ | 説明 |
---|---|
シーケンスコンテナ | 順序を持つデータの集合(例:vector, list) |
連想コンテナ | キーと値のペアでデータを管理(例:map, set) |
コンテナアダプタ | 既存のコンテナを別の形で利用するためのもの(例:stack, queue) |
STLコンテナを使用することで、データ構造の実装を自分で行う必要がなくなり、効率的なプログラミングが可能になります。
次のセクションでは、各コンテナの詳細について説明します。
シーケンスコンテナ
シーケンスコンテナは、データを順序付けて格納するためのコンテナです。
これにより、要素の追加、削除、アクセスが容易になります。
C++のSTLには、主に以下の3つのシーケンスコンテナがあります。
コンテナ名 | 説明 |
---|---|
vector | 動的配列で、サイズを変更可能。ランダムアクセスが高速。 |
list | 双方向リストで、要素の挿入・削除が高速。ランダムアクセスは遅い。 |
deque | 両端キューで、両端からの要素の追加・削除が高速。ランダムアクセスも可能。 |
vector
vector
は、動的にサイズを変更できる配列です。
要素の追加や削除が簡単で、ランダムアクセスが高速です。
以下は、vector
の基本的な使用例です。
#include <iostream>
#include <vector>
int main() {
std::vector<int> numbers; // 整数型のvectorを宣言
// 要素の追加
numbers.push_back(10); // 10を追加
numbers.push_back(20); // 20を追加
numbers.push_back(30); // 30を追加
// 要素の表示
for (int i = 0; i < numbers.size(); i++) {
std::cout << "要素 " << i << ": " << numbers[i] << std::endl; // 各要素を表示
}
return 0;
}
要素 0: 10
要素 1: 20
要素 2: 30
list
list
は、双方向リストで、要素の挿入や削除が高速です。
特に、リストの途中に要素を追加する場合に便利です。
以下は、list
の基本的な使用例です。
#include <iostream>
#include <list>
int main() {
std::list<int> numbers; // 整数型のlistを宣言
// 要素の追加
numbers.push_back(10); // 10を追加
numbers.push_back(20); // 20を追加
numbers.push_front(5); // 5を先頭に追加
// 要素の表示
for (int num : numbers) {
std::cout << "要素: " << num << std::endl; // 各要素を表示
}
return 0;
}
要素: 5
要素: 10
要素: 20
deque
deque
は、両端キューで、両端からの要素の追加や削除が高速です。
ランダムアクセスも可能で、特にキューやスタックの実装に適しています。
以下は、deque
の基本的な使用例です。
#include <iostream>
#include <deque>
int main() {
std::deque<int> numbers; // 整数型のdequeを宣言
// 要素の追加
numbers.push_back(10); // 10を追加
numbers.push_front(5); // 5を先頭に追加
numbers.push_back(20); // 20を追加
// 要素の表示
for (int num : numbers) {
std::cout << "要素: " << num << std::endl; // 各要素を表示
}
return 0;
}
要素: 5
要素: 10
要素: 20
シーケンスコンテナは、データの順序を保持しながら効率的に操作できるため、さまざまな場面で活用されます。
次のセクションでは、連想コンテナについて説明します。
連想コンテナ
連想コンテナは、キーと値のペアでデータを管理するためのコンテナです。
これにより、特定のキーを使用して迅速に値にアクセスできるため、データの検索や管理が効率的になります。
C++のSTLには、主に以下の4つの連想コンテナがあります。
コンテナ名 | 説明 |
---|---|
map | キーと値のペアを格納し、キーで値にアクセス。 |
multimap | 重複するキーを持つことができるmap。 |
set | 一意の要素を格納し、要素の存在を確認可能。 |
multiset | 重複する要素を持つことができるset。 |
map
map
は、キーと値のペアを格納する連想コンテナです。
キーは一意であり、値は任意の型を持つことができます。
以下は、map
の基本的な使用例です。
#include <iostream>
#include <map>
int main() {
std::map<std::string, int> ageMap; // 文字列をキー、整数を値とするmapを宣言
// 要素の追加
ageMap["Alice"] = 30; // Aliceの年齢を30に設定
ageMap["Bob"] = 25; // Bobの年齢を25に設定
// 要素の表示
for (const auto& pair : ageMap) {
std::cout << pair.first << "の年齢: " << pair.second << std::endl; // 各キーと値を表示
}
return 0;
}
Aliceの年齢: 30
Bobの年齢: 25
multimap
multimap
は、map
と似ていますが、同じキーを持つ複数の値を格納することができます。
以下は、multimap
の基本的な使用例です。
#include <iostream>
#include <map>
int main() {
std::multimap<std::string, int> ageMap; // 文字列をキー、整数を値とするmultimapを宣言
// 要素の追加
ageMap.insert({"Alice", 30}); // Aliceの年齢を30に設定
ageMap.insert({"Bob", 25}); // Bobの年齢を25に設定
ageMap.insert({"Alice", 35}); // Aliceの年齢を35に設定(重複キー)
// 要素の表示
for (const auto& pair : ageMap) {
std::cout << pair.first << "の年齢: " << pair.second << std::endl; // 各キーと値を表示
}
return 0;
}
Aliceの年齢: 30
Bobの年齢: 25
Aliceの年齢: 35
set
set
は、一意の要素を格納する連想コンテナです。
重複する要素は許可されず、要素の存在を確認するのに便利です。
以下は、set
の基本的な使用例です。
#include <iostream>
#include <set>
int main() {
std::set<int> numberSet; // 整数型のsetを宣言
// 要素の追加
numberSet.insert(10); // 10を追加
numberSet.insert(20); // 20を追加
numberSet.insert(10); // 重複する10は追加されない
// 要素の表示
for (const int& num : numberSet) {
std::cout << "要素: " << num << std::endl; // 各要素を表示
}
return 0;
}
要素: 10
要素: 20
multiset
multiset
は、set
と似ていますが、重複する要素を持つことができます。
以下は、multiset
の基本的な使用例です。
#include <iostream>
#include <set>
int main() {
std::multiset<int> numberSet; // 整数型のmultisetを宣言
// 要素の追加
numberSet.insert(10); // 10を追加
numberSet.insert(20); // 20を追加
numberSet.insert(10); // 重複する10を追加
// 要素の表示
for (const int& num : numberSet) {
std::cout << "要素: " << num << std::endl; // 各要素を表示
}
return 0;
}
要素: 10
要素: 10
要素: 20
連想コンテナは、データの検索や管理を効率的に行うために非常に便利です。
次のセクションでは、ハッシュコンテナについて説明します。
ハッシュコンテナ
ハッシュコンテナは、キーと値のペアを格納する連想コンテナの一種で、ハッシュテーブルを使用してデータを管理します。
これにより、データの検索、挿入、削除が平均的に非常に高速に行えるため、大量のデータを扱う際に特に有用です。
C++のSTLには、主に以下の2つのハッシュコンテナがあります。
コンテナ名 | 説明 |
---|---|
unordered_map | キーと値のペアを格納し、キーで値にアクセス。 |
unordered_set | 一意の要素を格納し、要素の存在を確認可能。 |
unordered_map
unordered_map
は、キーと値のペアを格納するハッシュコンテナです。
キーは一意であり、ハッシュ関数を使用してデータを管理します。
以下は、unordered_map
の基本的な使用例です。
#include <iostream>
#include <unordered_map>
int main() {
std::unordered_map<std::string, int> ageMap; // 文字列をキー、整数を値とするunordered_mapを宣言
// 要素の追加
ageMap["Alice"] = 30; // Aliceの年齢を30に設定
ageMap["Bob"] = 25; // Bobの年齢を25に設定
// 要素の表示
for (const auto& pair : ageMap) {
std::cout << pair.first << "の年齢: " << pair.second << std::endl; // 各キーと値を表示
}
return 0;
}
Aliceの年齢: 30
Bobの年齢: 25
unordered_set
unordered_set
は、一意の要素を格納するハッシュコンテナです。
重複する要素は許可されず、要素の存在を確認するのに便利です。
以下は、unordered_set
の基本的な使用例です。
#include <iostream>
#include <unordered_set>
int main() {
std::unordered_set<int> numberSet; // 整数型のunordered_setを宣言
// 要素の追加
numberSet.insert(10); // 10を追加
numberSet.insert(20); // 20を追加
numberSet.insert(10); // 重複する10は追加されない
// 要素の表示
for (const int& num : numberSet) {
std::cout << "要素: " << num << std::endl; // 各要素を表示
}
return 0;
}
要素: 10
要素: 20
ハッシュコンテナは、データの検索や管理を高速に行うために非常に便利であり、大量のデータを扱うアプリケーションにおいて特に効果を発揮します。
次のセクションでは、コンテナアダプタについて説明します。
コンテナアダプタ
コンテナアダプタは、既存のコンテナを別の形で利用するためのラッパーです。
これにより、特定のデータ構造の特性を活かしつつ、異なるインターフェースを提供します。
C++のSTLには、主に以下の3つのコンテナアダプタがあります。
アダプタ名 | 説明 |
---|---|
stack | LIFO(Last In, First Out)構造を持つスタック。 |
queue | FIFO(First In, First Out)構造を持つキュー。 |
priority_queue | 優先度に基づいて要素を管理するキュー。 |
stack
stack
は、LIFO(Last In, First Out)構造を持つコンテナアダプタです。
最後に追加した要素が最初に取り出されます。
以下は、stack
の基本的な使用例です。
#include <iostream>
#include <stack>
int main() {
std::stack<int> numberStack; // 整数型のstackを宣言
// 要素の追加
numberStack.push(10); // 10を追加
numberStack.push(20); // 20を追加
numberStack.push(30); // 30を追加
// 要素の表示と削除
while (!numberStack.empty()) {
std::cout << "スタックのトップ: " << numberStack.top() << std::endl; // トップ要素を表示
numberStack.pop(); // トップ要素を削除
}
return 0;
}
スタックのトップ: 30
スタックのトップ: 20
スタックのトップ: 10
queue
queue
は、FIFO(First In, First Out)構造を持つコンテナアダプタです。
最初に追加した要素が最初に取り出されます。
以下は、queue
の基本的な使用例です。
#include <iostream>
#include <queue>
int main() {
std::queue<int> numberQueue; // 整数型のqueueを宣言
// 要素の追加
numberQueue.push(10); // 10を追加
numberQueue.push(20); // 20を追加
numberQueue.push(30); // 30を追加
// 要素の表示と削除
while (!numberQueue.empty()) {
std::cout << "キューのフロント: " << numberQueue.front() << std::endl; // フロント要素を表示
numberQueue.pop(); // フロント要素を削除
}
return 0;
}
キューのフロント: 10
キューのフロント: 20
キューのフロント: 30
priority_queue
priority_queue
は、要素に優先度を持たせ、優先度の高い要素が先に取り出されるように管理するコンテナアダプタです。
以下は、priority_queue
の基本的な使用例です。
#include <iostream>
#include <queue>
int main() {
std::priority_queue<int> numberQueue; // 整数型のpriority_queueを宣言
// 要素の追加
numberQueue.push(10); // 10を追加
numberQueue.push(30); // 30を追加
numberQueue.push(20); // 20を追加
// 要素の表示と削除
while (!numberQueue.empty()) {
std::cout << "優先度キューのトップ: " << numberQueue.top() << std::endl; // トップ要素を表示
numberQueue.pop(); // トップ要素を削除
}
return 0;
}
優先度キューのトップ: 30
優先度キューのトップ: 20
優先度キューのトップ: 10
コンテナアダプタは、特定のデータ構造の特性を活かしつつ、異なる操作を簡単に行えるようにするための便利なツールです。
次のセクションでは、コンテナ選択の基準について説明します。
コンテナ選択の基準
C++のSTLには多くのコンテナがあり、それぞれ異なる特性を持っています。
適切なコンテナを選択することは、プログラムの効率性や可読性に大きな影響を与えます。
以下の基準を考慮して、使用するコンテナを選択することが重要です。
基準 | 説明 |
---|---|
データの順序 | データを順序付けて格納する必要があるか、またはキーでアクセスする必要があるか。 |
挿入・削除の頻度 | 要素の挿入や削除が頻繁に行われる場合、どのコンテナが最も効率的か。 |
アクセスの速度 | 要素へのアクセスがどれだけ頻繁に行われるか。ランダムアクセスが必要か、または順次アクセスで十分か。 |
メモリの使用量 | コンテナのメモリ使用量が重要な場合、どのコンテナが最も効率的か。 |
要素の重複 | 重複する要素を許可する必要があるか、または一意の要素のみを扱うか。 |
データの順序
データを順序付けて格納する必要がある場合は、vector
やlist
などのシーケンスコンテナが適しています。
一方、キーでアクセスする必要がある場合は、map
やunordered_map
などの連想コンテナが適しています。
挿入・削除の頻度
要素の挿入や削除が頻繁に行われる場合、list
やdeque
が適しています。
これらのコンテナは、要素の挿入や削除が高速です。
逆に、vector
は末尾への追加は高速ですが、途中への挿入や削除は遅くなります。
アクセスの速度
要素へのアクセスが頻繁に行われる場合、vector
やdeque
が適しています。
これらのコンテナはランダムアクセスが高速です。
一方、list
はランダムアクセスが遅いため、順次アクセスが主な場合に使用するのが良いでしょう。
メモリの使用量
メモリの使用量が重要な場合、vector
は必要に応じてサイズを変更できるため、メモリの効率が良いです。
list
は各要素にポインタを持つため、メモリのオーバーヘッドが大きくなります。
要素の重複
重複する要素を許可する必要がある場合は、multiset
やmultimap
を使用します。
一意の要素のみを扱う場合は、set
やmap
を選択します。
これらの基準を考慮することで、プログラムの要件に最も適したコンテナを選択し、効率的なデータ管理を実現できます。
次のセクションでは、STLコンテナの共通機能について説明します。
STLコンテナの共通機能
STLコンテナは、それぞれ異なる特性を持っていますが、多くのコンテナには共通の機能が備わっています。
これにより、異なるコンテナを使用する際でも、同様の操作が可能となり、プログラムの可読性と保守性が向上します。
以下に、STLコンテナの主な共通機能を示します。
機能名 | 説明 |
---|---|
要素の追加 | コンテナに新しい要素を追加する機能。 |
要素の削除 | コンテナから要素を削除する機能。 |
要素のアクセス | コンテナ内の要素にアクセスする機能。 |
サイズの取得 | コンテナ内の要素数を取得する機能。 |
空かどうかの確認 | コンテナが空であるかどうかを確認する機能。 |
反復子(イテレータ) | コンテナ内の要素を順に処理するための機能。 |
要素の追加
ほとんどのSTLコンテナには、要素を追加するためのメソッドが用意されています。
例えば、push_back()
やinsert()
メソッドを使用して、新しい要素をコンテナに追加できます。
要素の削除
要素を削除するためのメソッドも多くのコンテナに存在します。
pop_back()
やerase()
メソッドを使用して、特定の要素や最後の要素を削除することができます。
要素のアクセス
STLコンテナは、要素にアクセスするためのメソッドを提供しています。
at()
やoperator[]
を使用して、特定のインデックスやキーに基づいて要素にアクセスできます。
サイズの取得
size()
メソッドを使用することで、コンテナ内の要素数を簡単に取得できます。
これにより、コンテナの状態を把握することができます。
空かどうかの確認
empty()
メソッドを使用して、コンテナが空であるかどうかを確認できます。
これにより、要素が存在するかどうかを簡単に判断できます。
反復子(イテレータ)
STLコンテナは、反復子(イテレータ)を使用して要素を順に処理することができます。
begin()
とend()
メソッドを使用して、コンテナの最初と最後の要素を指すイテレータを取得し、ループを使用して要素を処理できます。
これらの共通機能を理解することで、異なるSTLコンテナを効果的に利用し、プログラムの効率性を向上させることができます。
まとめ
この記事では、C++のSTLコンテナについて、シーケンスコンテナ、連想コンテナ、ハッシュコンテナ、コンテナアダプタ、コンテナ選択の基準、そして共通機能について詳しく解説しました。
これにより、各コンテナの特性や使用方法を理解し、適切なコンテナを選ぶための基準を把握することができるでしょう。
今後は、実際のプログラミングにおいて、これらの知識を活用し、効率的なデータ管理を行ってみてください。