UNDER THE EDGE

ApexでのMapの使い方を徹底解説!【Salesforce】

こんにちは、アンダーソンです。
今回はApexにおけるMapの使い方を説明していきたいと思います。
以前にApexのList,Map,Setの使い方を徹底解説!という記事を書いたんですが、
Mapを使いこなすことによるメリットはかなり大きいので
是非いろんな使い方をマスターしていくためにまとめておきたいと思います。

Mapを使いこなすと下記のようなメリットがあります。

ガバナ制限の回避をしやすくなる
Apexの実行時間の制限を回避しやすくなる
複雑なロジックにも対応しやすくなる

と言ったことです。
ではそれぞれどのように使っていくのかを実際のコードをみながら
解説していきたいと思います。

Mapの宣言と入れられる値

まず、Mapの基本的な使い方です。
ApexのList,Map,Setの使い方を徹底解説!
でも解説をしているのでおさらいになりますが、Mapは

キー=値

という形で格納されているものでしたね。
キーにくるのはプリミティブ型でもコレクションでも、sObject型でも
OKです。
また値も同じくです。
実際の宣言をみてみましょう。

Map<String, String> sampleMap1 = new Map<String, String>();
Map<Id, Account> sampleMap2 = new Map<Id, Account>();
Map<Integer, List<String>> sampleMap3 = new Map<Integer, List<String>>();
Map<Id, Map<String,sObject>> sampleMap4 = new Map<Id, Map<String,sObject>>();

このようにMapは対応付け、キーに対して値がある形なので、
Stringに対してStringの値、Idに対してAccountというように様々な形を表現できます。

初めからキーと値を宣言しておくこともできます。

Map<String, String> sampleMap1 = new Map<String, String>{
    'a' => 'A', 'b' => 'B' , 'c' => 'C'
};
Map<Id, Account> sampleMap2 = new Map<Id, Account>([SELECT Id FROM Account]);

このように対応付けを宣言時に入れておくことも可能です。
後から対応付けを入れる場合にはputを使います。

Map<Id, Account> sampleMap2 = new Map<Id, Account>();

for ( Account acc : [SELECT Id, Name FROM Account] ) {
    sampleMap2.put(acc.Id,acc);
}

初期化時点で入れておく方が圧倒的に楽なので前者のやり方は
覚えておく方がいいと思います(無駄にfor文を回さなくて済むメリットもあり)

値を取り出すには?

Mapのメリットはキー=値ということです。
Listはindexという番号が0から振られており、
例えば1万件のListがあったとして、5001件目にアクセスしたい値がある場合、
それを探し出さないといけません。

List<Integer> intList = new List<Integer>{1,2,3,4 ... 10000};
Boolean success = false;

for ( Integer i : intList ) {
    if ( i == 5001 ) {
        success = true;
        break;
    }
}

このような形だと、時間がかかってしまい非常に非効率です。
Mapの場合、キーさえあれば値を取り出すことができるので、
時間をかけることなく上記のようなことができます。

Map<Integer,Integer> intMap = new Map<Integer,Integer>{1 => 1 ,2 => 2 ... 10000 => 10000};
Boolean success = false;

if ( intMap.get(5001) != null ) success = true;

.get(key)で値を取り出すことができますし、なければnullが返ってくるので、
Listの場合よりも簡単にしかも早く結果を調べることができます。

また二つの値を比較するような場合でも効果があります。
たとえばListの場合

List<Account> accList = [SELECT Id, Name FROM Account];
List<Contact> conList = [SELECT AccountId FROM Contact];

for ( Account acc : accList ) {
    for ( Contact con : conList ) {
        if ( acc.Id = con.AccountId ) {
            // 処理を書く
        }
    }
}

このような場合、リストの件数×リストの件数分ループが回ってしまうため、
トリガやバッチなどで大量レコードを扱う際にはあまりいい方法とは言えません。
これがMapの場合だと簡単にできます。

List<Contact> conList = [SELECT AccountId FROM Contact];
Map<Id,Account> accMap = new Map<Id,Account>([SELECT Id FROM Account]);

for ( Contact con : conList ) {
    if ( accMap.get(con.AccountId) != null ) {
        //ここに処理を書く 
    }
}

これでループ自体を1ブロック分減らしかつ、ダイレクトにMapの中をみにいくので
処理時間をかなり軽減させることができます。

他にも便利な使い方

他にもキーや値でループを回すこともできます。

Map<Id,Account> accMap = new Map<Id,Account>([SELECT Id FROM Account]);

for ( Id accId : accMap.keyset() ) {
    // 何か処理を書く
}
for ( Account acc : accMap.values() ) {
    // 何か処理を書く
}

任意の値を含んでいるかの確認の場合は下記のように書きます。

accMap.containsKey(key); //trueもしくはfalseで返ってくる

また、下記のようなこともできます。

Map<Id,Account> accMap = new Map<Id,Account>([SELECT Id FROM Account]);

for ( Id accId : accMap.keyset() ) {
    accMap.get(accId).Industry = 'Agriculture'; 
}
update accMap.values();

わざわざリストにしなくてもまとめてvaluesでDML操作もしちゃえます。
valuesはループも回せますが、中の値のDML操作は1としてカウントできるので便利です。

まとめ

今回はListよりも実は知っておくべきMapについてより詳しく解説しました。
是非用途に応じて使いこなせるようになっていきましょう!

その他の開発に関する記事はこちらです。

Developerも含めた試験問題にチャレンジしてみましょう。


Salesforce
Account Engagement
CRM Analyticsの導入支援、
AppExchange
開発支援でお悩みなら
今すぐご相談ください

お問い合わせはこちら