🔽 【Apex】List / Set / Map の特徴・違い・使い方まとめ
Apex でよく使うコレクション型 List / Set / Map の特徴・違い・使い方をまとめました。
✅ まず押さえるべきポイント💡
- List : インデックスで識別される要素の順序付けされたコレクション
- Set : 重複を含まない要素の順序付けされていないコレクション
- Map : 単一の値に一意のキーを対応付ける、キー - 値のペアのコレクション
1. Listとは
Apex において 最も基本的に使うコレクション型 が List
です。
順序付きで、重複を許容する点が最大の特徴です。
- 順序を保持(インデックス 0 から順番にアクセス可能)
- 同じ値を何度でも格納できる(重複あり)
- SOQL のクエリ結果は基本的に
List<SObject>
で返される
🔹 Listの使い方
// 初期化
List<String> fruits = new List<String>();
// 挿入
fruits.add('Apple');
fruits.add('Banana');
fruits.add('Apple'); // 重複OK
// 取得
System.debug(fruits); // => (Apple, Banana, Apple)
System.debug(fruits[0]); // => "Apple"
System.debug(fruits.size()); // => 3
// 値を更新(2番目を Mango に変更)
fruits[1] = 'Mango';
// 要素を削除(0番目を削除)
fruits.remove(0);
System.debug(fruits); // => (Mango, Apple)
👉 List の代表的なメソッド一覧の詳細はこちらから
https://developer.salesforce.com/docs/atlas.ja-jp.apexcode.meta/apexcode/apex_methods_system_list.htm#apex_System_List_methods
🔹 Listの利用例
- 順序を保持する必要があるデータ(例: タスクの処理順、履歴の記録)
- 重複を許容するデータ(例: 注文リスト、履歴ログ)
- SOQL 結果の格納(例: List contacts = [SELECT Id, Name FROM Contact];)
2. Setとは
Apex において ユニークな値を保持したいときに使うコレクション型 が Set
です。
「順序は保持せず、重複を許さない」点が最大の特徴です。
- 順序を保持しない(インデックスアクセス不可)
- 重複した値は自動的に排除される
- 値の存在確認が高速
🎯 ポイント
「同じ値を 2 回入れても 1 つにまとまる」というのが最大のポイント!
Setの使い方
// 初期化
Set<String> fruits = new Set<String>();
// 挿入
fruits.add('Apple');
fruits.add('Banana');
fruits.add('Apple'); // 重複は無視される
System.debug(fruits); // => {Apple, Banana}
// 存在確認
System.debug(fruits.contains('Apple')); // => true
System.debug(fruits.contains('Mango')); // => false
// 値の削除
fruits.remove('Apple');
System.debug(fruits); // => {Banana}
⚠️ Set はインデックスを持たないため、fruits[0] のようなアクセスはできません。
重複排除の目的で、SOQL の結果 List を Set に変換可能
🔹 SOQL との組み合わせ
// 重複ありの List
List<Id> contactIds = new List<Id>{'000XXX1', '000XXX2', '000XXX1'};
// Set に変換して重複排除
Set<Id> uniqueContactIds = new Set<Id>(contactIds);
System.debug(uniqueContactIds); // => {'003XXX1', '003XXX2'}
🔹 Set の便利メソッド
Set<Integer> numbers = new Set<Integer>{1, 2, 3};
Set<Integer> others = new Set<Integer>{3, 4, 5};
// 結合(和集合)
numbers.addAll(others);
// => {1, 2, 3, 4, 5}
// 共通部分(積集合)
Set<Integer> common = numbers.clone();
common.retainAll(others);
// => {3, 4, 5}
// 差集合
Set<Integer> diff = numbers.clone();
diff.removeAll(others);
// => {1, 2}
👉 Set の代表的なメソッド一覧の詳細はこちらから
https://developer.salesforce.com/docs/atlas.ja-jp.apexcode.meta/apexcode/apex_methods_system_set.htm#apex_System_Set_methods
🔹 Setの利用例
1. メールアドレスの重複排除
List<String> inputEmails = new List<String>{
'test@example.com',
'user@example.com',
'test@example.com' // 重複あり
};
// Set に変換して重複排除
Set<String> uniqueEmails = new Set<String>(inputEmails);
System.debug(uniqueEmails);
// => {test@example.com, user@example.com}
🎯 ポイント
「一斉メール送信処理」で、同じメールアドレスに複数回送らないように使用できる。
2. ID の集合管理(SOQL の IN 条件で利用)
// Contact を検索
List<Contact> contacts = [SELECT Id, AccountId FROM Contact WHERE Email != null];
// AccountId を Set に変換(重複自動排除)
Set<Id> accIds = new Set<Id>();
for(Contact c : contacts){
accIds.add(c.AccountId);
}
// 重複しない Account を一括取得
List<Account> accounts = [SELECT Id, Name FROM Account WHERE Id IN :accIds];
List で取得したデータから ID を取り出し、Set にまとめて SOQL の IN 条件 に使用。
👉 これにより「関連する Account を重複なく取得する」ことができます。
3. 存在チェックの高速化
// すでに登録済みの Email 一覧
Set<String> registeredEmails = new Set<String>{
'a@example.com', 'b@example.com'
};
// 新しいユーザを登録する処理
String newEmail = 'a@example.com';
if(registeredEmails.contains(newEmail)){
System.debug('すでに登録済みです');
} else {
System.debug('新規登録可能です');
}
🎯 ポイント
大量のデータを処理するとき、Set は 「この値が含まれているか?」を一瞬で判定
👉 登録済みかどうか判定してバリデーションや二重登録を防止
3. Mapとは
Apex において Key と Value を対応付けて管理できるコレクション型 が Map
です。
「Key は一意で重複不可、Value は重複可」というのが最大の特徴です。
// 基本構文
Map<KeyType, ValueType> 変数名 = new Map<KeyType, ValueType>();
// KeyType : キーの型(例: Id, String, Integer など)
// ValueType : 値の型(例: String, Account, List<Contact> など)
Mapの使い方
// 初期化
Map<String, String> countryCapital = new Map<String, String>();
// 挿入
countryCapital.put('Japan', 'Tokyo');
countryCapital.put('France', 'Paris');
countryCapital.put('Japan', 'Kyoto'); // 同じキーを上書き
// Key を指定して取得
System.debug(countryCapital.get('Japan')); // => "Kyoto"
System.debug(countryCapital.containsKey('France')); // => true
🔎 List vs Map 検索処理の比較
✅ List で検索の場合
- 要素を順番に調べていく必要がある
- データが増えるほどループが長くなる(効率が悪い)
// Contactリストを取得
List<Contact> contacts = [SELECT Id, Name FROM Contact LIMIT 5];
// 探したいId
Id targetId = contacts[0].Id;
Contact foundContact;
// 検索処理(ループが必要)
for(Contact c : contacts){
if(c.Id == targetId){
foundContact = c;
break; // 見つかったら終了
}
}
System.debug('List検索結果: ' + foundContact.Name);
✅Map で検索の場合
- Id を Key にして SObject を一発検索
// ContactをMap化(IdをKey)
Map<Id, Contact> contactMap = new Map<Id, Contact>(
[SELECT Id, Name FROM Contact LIMIT 5]
);
// 探したいId(ここでは仮のIdを直接指定)
Id targetId = '003XXXXXXXXXXXX';
// 検索処理(getで一発)
Contact foundContact = contactMap.get(targetId);
System.debug('Map検索結果: ' + foundContact.Name);
🔹 Mapの利用例
1. SOQL 結果を ID 紐付けで高速検索
// Contact のリスト取得
List<Contact> cons = [SELECT Id, AccountId, LastName FROM Contact];
// 関連する Account を一括取得 → Map化
Map<Id, Account> accMap = new Map<Id, Account>(
[SELECT Id, Name FROM Account
WHERE Id IN :new Map<Id, Contact>(cons).values().AccountId]
);
// Contact に関連する Account 名を参照
for(Contact c : cons){
Account a = accMap.get(c.AccountId); // Idから一発検索
System.debug(c.LastName + ' さんの会社は ' + a.Name);
}
📌 解説
- new Map(cons) で Contact リストをそのまま Map 化できる
- その values() を使って AccountId をまとめて抽出し、関連 Account を一括取得
- accMap.get(c.AccountId) で ループ内 SOQL 不要 & 高速検索が可能
🎯 ポイント
SOQL で取得した SObject を Map に変換すると、ID をキーにして直接参照できるようになる
2. 親子データのひも付け処理
// ① Contact を取得(AccountId付き)
List<Contact> cons = [SELECT Id, AccountId FROM Contact LIMIT 50];
// ② Account を Map 化
Set<Id> accIds = new Set<Id>();
for(Contact c : cons){
if(c.AccountId != null){
accIds.add(c.AccountId);
}
}
Map<Id, Account> accMap = new Map<Id, Account>(
[SELECT Id, Name FROM Account WHERE Id IN :accIds]
);
// ③ Case を作成する際に Account 名を参照
List<Case> newCases = new List<Case>();
for(Contact c : cons){
if(accMap.containsKey(c.AccountId)){
newCases.add(new Case(
ContactId = c.Id,
Subject = '問い合わせ: ' + accMap.get(c.AccountId).Name
));
}
}
insert newCases;
📌 解説
• Contact から AccountId を集めておき、一括 SOQL で Account を取得 → Map 化
• ループ内で accMap.get(c.AccountId) により 関連する Account 名をすぐ参照
• これにより N+1 SOQL 問題を回避しつつ、親子データのひも付けが簡単になる
まとめポイント
特徴
- List : 順序重視・重複OK
- Set : ユニーク性重視・順序不要・存在チェックが高速
- Map : キーで値を管理・キーはユニーク・SOQL結果の効率的アクセスに便利
使い分け
- List : タスク処理順や履歴記録、重複データを扱うとき
- Set : メールアドレスの重複排除、ID集合管理、存在チェック
- Map : SObjectをIDで参照したい場合、関連情報の高速取得