【Apex】List / Set / Mapの使い方まとめ

# はじめに
本記事では、Apexでコーディングする際は必ずと言っていいほど使用する3つのクラスについて、備忘録も兼ねて基本的な使い方をまとめています。
まず簡単に特徴についてまとめた後で、意外と知らない？便利な使い方をいくつか紹介します。

本記事はコーディング経験がほとんど無い方から、少しコーディングに慣れてきた方あたりを対象としています。

# List
https://developer.salesforce.com/docs/atlas.ja-jp.apexcode.meta/apexcode/apex_methods_system_list.htm#apex_methods_system_list

### 特徴
複数の値やSObjectを保持するクラスで、Setと違い順番（Index）を保持している事が特徴です。

### 初期化
```java
List<String> strList1 = new List<String>(); // 基本
List<String> strList2 = new List<String>(5); // サイズを5に指定
List<String> strList3 = new List<String>{'a', 'b', 'c', 'd', 'e'}; // 初期値指定
List<String> strList4 = new List<String>(strList3); // Listをコピー（Setでも可）
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/573865/fc1ac76c-d8d4-2040-ec6a-f238dc7baa9d.png)



# Set
https://developer.salesforce.com/docs/atlas.ja-jp.apexcode.meta/apexcode/apex_methods_system_set.htm

### 特徴
複数の値を保持する点はListと同じです。
Listと違い順番は保持していない代わりに、Set内の値は一意である（重複しない）事が特徴です。

### 初期化
```java
Set<String> strSet1 = new Set<String>(); // 基本
Set<String> strSet2 = new Set<String>{'a', 'b', 'c', 'd', 'e'}; // 初期値指定
Set<String> strSet3 = new Set<String>(strSet2); // Setをコピー

List<String> strList = new List<String>{'a', 'A', 'A', 'b', 'c', 'f', 'g'}; // 重複有りのList
Set<String> strSet4 = new Set<String>(strList); // 大文字小文字が区別され、Listの順番が破棄される
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/573865/1a042b81-b603-5205-27a3-7f320c5fe367.png)

# Map
https://developer.salesforce.com/docs/atlas.ja-jp.apexcode.meta/apexcode/apex_methods_system_map.htm

### 特徴
キーと値の対応付けを保持でき、`testMap.get('キー名')`と書けば値を取れるためループを回す必要がありません。

### 初期化
```java
Map<String, String> strMap1 = new Map<String, String>(); // 基本
Map<String, String> strMap2 = new Map<String, String>{'A' => 'a', 'B' => 'b', 'C' => 'c', 'D' => 'd', 'E' => 'e'}; // 初期値指定
Map<String, String> strMap3 = new Map<String, String>(strMap2); // Mapをコピー

List<Account> accList = [SELECT Id, Name FROM Account WHERE Name = 'test' LIMIT 3]; // Accountレコードを取得
Map<Id, Account> accMap = new Map<Id, Account>(accList); // accListをコピー
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/573865/50fcd221-89e2-e0dc-2d4e-235af0ec0da9.png)

# 便利な使い方

### 1. SOQLの結果を直接Mapに代入

```java
Map<Id, Account> accMap = new Map<Id, Account>(
    [SELECT Id, Name FROM Account WHERE Name LIKE 'test' LIMIT 3]
);
```
上記に限らず`()`内にSObject型のListが入ればOKなので、自身で定義したメソッドを初期値に指定することも可能です。

```java
// paramを検索条件にAccountを取得するクラス
public class SampleAccountManager {
    public static List<Account> getAccount(String param){
        return [
            SELECT
                Id,
                Name
            FROM
                Account
            WHERE
                Name = :param
            LIMIT 3
        ];
    }
}

// 別クラスから呼び出し
Map<Id, Account> accMap = new Map<Id, Account>(SampleAccountManager.getAccount('test'));
```


### 2. Mapの値からListを生成
```java
Map<String, String> strMap = new Map<String, String>{'A' => 'a', 'B' => 'b', 'C' => 'c', 'D' => 'd', 'E' => 'e'};
List<String> strList = new List<String>(strMap.values());

// strList => (a, b, c, d, e)
```

`values()`の返り値はList型なので、以下のような使い方も可能です。

```java
List<String> strList = new List<String>{'あ', 'い', 'う', 'え', 'お'};
Map<String, String> strMap = new Map<String, String>{'A' => 'a', 'B' => 'b', 'C' => 'c', 'D' => 'd', 'E' => 'e'};

// strMapの値をループで回す
for(String s : strMap.values()){
    System.debug(s);
}

// strMapの値を全てstrListへ追加
strList.addAll(strMap.values());

// strList => (あ, い, う, え, お, a, b, c, d, e)
```

### 3. MapのキーからSetを生成
```java
Map<String, String> strMap = new Map<String, String>{'A' => 'a', 'B' => 'b', 'C' => 'c', 'D' => 'd', 'E' => 'e'};
Set<String> strSet = new Set<String>(strMap.keyset());

// strSet => {A, B, C, D, E}
```

蛇足ですが、上記Listの例に類似する使い方が可能です。

```java
Set<String> strSet = new Set<String>{'A', 'B', 'C'};
Map<String, String> strMap = new Map<String, String>{'A' => 'a', 'B' => 'b', 'C' => 'c', 'D' => 'd', 'E' => 'e'};

// strMapのキーをループで回す
for(String s : strMap.keyset()){
    System.debug(s);
}

// strMapの値を全てstrSetへ追加
strSet.addAll(strMap.keyset());

// strSet => {A, B, C, D, E}
```

### 4. Mapのソート
まず、Map型はList型と同様に順番を保持しています。

> 対応付け要素の順序は確定的です。順序は、同じコードの後続のどの実行でも同じです。たとえば、values() メソッドで、value1 とインデックス 0 および value2 とインデックス 1 を含むリストが返されるとします。その後同じコードを実行した場合も、これらの値が同じ順序で返されます。  
[Apex開発者ガイド / Mapクラス / values()](https://developer.salesforce.com/docs/atlas.ja-jp.230.0.apexcode.meta/apexcode/apex_methods_system_map.htm#apex_System_Map_values)

適当な順番でMapを生成すると、そのままの順番が保持されています。

```java
// キーの2乗を値として保持するMap
Map<Integer, Integer> intMap = new Map<Integer, Integer>{4 => 16, 3 => 9, 1 => 1, 5 => 25, 2 => 4};

for(Integer i : intMap.keyset()){
    System.debug('キー = ' + i + ' ： 値 = ' + intMap.get(i));
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/573865/e04fbdfb-df68-f468-aa5d-df1b821fb1af.png)



順番通り並べ替えを行いたいところですが、Map型には並べ替え用のメソッドが用意されていません。
そのためsortメソッドが用意されているList型にキーを代入してから並び替えを行い、そのListを使用してMapから値を取り出すことで並び替えを行うことができます。

```java
// キーの2乗を値として保持するMap
Map<Integer, Integer> intMap = new Map<Integer, Integer>{4 => 16, 3 => 9, 1 => 1, 5 => 25, 2 => 4};

// Mapのキーを一時的に保持するList
List<Integer> sortList = new List<Integer>(intMap.keyset());

System.debug('sort前 ：' + sortList);

// Listクラスのsortメソッドを使用して並べ替え
sortList.sort();

System.debug('sort後 ：' + sortList);

// 新しいMapを定義
Map<Integer, Integer> sortedMap = new Map<Integer, Integer>();

// sort後のListをループで回し、sort前のMapから値を取り出す
for(Integer i : sortList){
    sortedMap.put(i, intMap.get(i));
}

for(Integer i : sortedMap.keyset()){
    System.debug('キー = ' + i + ' ： 値 = ' + sortedMap.get(i));
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/573865/98de9a5e-9704-a9e5-2708-5c6d747a0725.png)

# 最後に
クラスの特徴を押さえて使い分ける事ができるようになってくると、より簡潔にコードを書けるようになり、ソースコードの見通しも良くなってくると思います。
またループ処理の使用頻度が減ることにより処理時間が短縮され、ユーザビリティの向上にも繋がるので、今回紹介した記法についてはぜひ使いこなして下さい！