Saturday, 17 September 2016

Collection in salesforce | example using LIST and MAP | When to use Map Over List | When do we use set, map, list



While working on Developer forum i found lots of question for List ,Set and Map,  like below
1) When we should use Map over the list
2) When do we use set, Map,List
3) Bulkify code with Map
4) Using Maps and Sets in Bulk Triggers
5) Use of Map in Apex class.
6) Map over list to avoid query inside for loop

Solution :-

List, Set, Map are called collections in Apex:
List : A list is an ordered collection
1) so use list when you want to identify list element based on Index Number.
2) list can contain Duplicates

EX: List<Account> accList = new List<Account>();

Set : A set is an unordered collection
1) Do not contain any duplicate elements. So, use set if you want to make sure that your collection should not contain Duplicates.

EX: Set<Account> accSet = new Set<Account>()

 
Set<String> setString = new Set<String>();
// Add two strings to it
setString .add('item1');
setString .add('item2');

Map : A map is a collection of key-value pairs 
Each unique key maps to a single value. Keys can be any primitive data type, while values can be a primitive, sObject, collection type or an Apex object.

EX: Map<Id, Account> accMap = new Map<Id, Account>();

Map<Integer, String> mapOfString = new Map<Integer, String>();
mapOfString.put(1, 'Amit');               
mapOfString.put(2, 'Rahul');              
System.assert(mapOfString.containsKey(1)); 
String value = mapOfString.get(2); 
System.assertEquals('Rahul', value);
Set<Integer> s = mapOfString.keySet();



Map<String, String> myMap = new Map<String, String>{'a' => 'b', 'c' => 'd'};






Example 1:-  Using trigger populate the Account Field on the Contact record (only insert scenario)

If we use List and not Map

Apex Class

trigger ContactTriggerWithList on Contact (before insert) 
{
Set<Id> SetAccountId = new Set<Id>(); // Use set to collect unique account ID

for(Contact con: Trigger.new) 
{
 if(con.AccountId != null) 
 {
  SetAccountId.add(con.AccountId); // add Account in Set
 }
}

if( SetAccountId.size() >0 ) 
{
 List<Account> listAccount = [ Select Id, Name, Type from Account where Id IN :SetAccountId ]; // Query Related Account
 
 for(Contact con: Trigger.new) 
 {
  if(con.AccountId != null)
  {
   for(Account acc: listAccount)
   {
    if(con.AccountId == acc.Id)
    {
     con.Type__c = acc.Type;
    }
   }
  }
 }
} 
}

 
NOTE:- In this we are using List of Accounts, Where we have to loop through the matching Account every time to populate the Type__c in the second for loop. In this case need to use nested loop.

To Above the Nested Loop We can use the Map .

Same Trigger using the Map:

trigger ContactTriggerWithMap on Contact (before insert) 
{
    Set<Id> SetAccountId = new Set<Id>();
    for(Contact cont: Trigger.new) 
 {
        if(cont.AccountId != null) 
  {
            SetAccountId.add(cont.AccountId);
        }
    }
    
    if(SetAccountId.size() > 0 ) 
 {
        Map<Id, Account> mapAccount = new Map<Id, Account>([Select Id, Name, Type from Account where Id IN :SetAccountId ]);
        for(Contact cont: Trigger.new) 
  {
            if(cont.AccountId != null && mapAccount.containsKey(cont.AccountId) ) 
   {
                cont.Type__c = mapAccount.get(cont.AccountId).Type;
            }
        }
    }
}

NOTE:- Here we are using map of Account so we can directly use the Map to get the Account record. No need of 2nd for loop here



Related Link :-
1) https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/langCon_apex_collections.htm
2) https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_triggers_bulk_idioms.htm
3) https://developer.salesforce.com/page/Apex_Code_Best_Practices


Thanks,
Amit Chaudhary