5

I have a map of Id to List of Sobject. To update the map i use below code snippet

for(SObject so : listSObject) {
    if(mapSO.get(so.ParentId) != NULL)
        mapSO.get(so.PartentId).add(so);
    else
        mapSo.put(so.ParentId,new List<SObject>{so});
}

I came across a code like this

for(SObject so : listSObject) { 
    List<SObject> lstSO = new List<SObject>();
    if(mapSO.containsKey(so.ParentId))
        lstSO = mapSO.get(so.PartentId);
    lstSO.add(so);
    mapSo.put(so.ParentId,lstSO);
}

My question is which is the best way to use? What are the advantages over one another? Any performance related differences?

4 Answers 4

6

Here is another common way to accomplish this:

for (SObject so : listSObject) {
    List<SObject> listSo = mapSO.get(so.PartentId);
    if (listSo == null) {
        listSo = new List<SObject>();
        mapSo.put(so.PartentId, listSo);
    }
    listSo.add(so);
}

Working code is what matters, but I'll mention that this does a single get rather than a contains and then a get (in the common case of the list already being in the map) so if the map key lookup is expensive (with a normal hash map first the hashCode has to be converted to a bucket index then the list at that index searched for the key by invoking equals on each entry) that may help performance.

5

I tried all the suggestions with an example and found the following results.

Map<Id,List<Contact>> mapContactIdLstAccount = new Map<Id,List<Contact>>();
system.debug(system.now() + ' ' + system.now().millisecond());
for(Contact c : [select AccountId from Contact limit 200 offset 0]){
    List<Contact> lstContact = new List<Contact>();
    if(mapContactIdLstAccount.containsKey(c.AccountId))
        lstContact = mapContactIdLstAccount.get(c.AccountId);
    lstContact.add(c);
    mapContactIdLstAccount.put(c.AccountId,lstContact);
}
system.debug(system.now() + ' ' + system.now().millisecond());

281 Milliseconds - From original Question

Map<Id,List<Contact>> mapContactIdLstAccount = new Map<Id,List<Contact>>();
system.debug(system.now() + ' ' + system.now().millisecond());
for(Contact c : [select AccountId from Contact limit 200 offset 0]){
    if(mapContactIdLstAccount.get(c.AccountId) != NULL)
        mapContactIdLstAccount.get(c.AccountId).add(c);
    else
        mapContactIdLstAccount.put(c.AccountId,new List<Contact>{c});
}
system.debug(system.now() + ' ' + system.now().millisecond());

154 Milliseconds - From original Question

Map<Id,List<Contact>> mapContactIdLstAccount = new Map<Id,List<Contact>>();
system.debug(system.now() + ' ' + system.now().millisecond());
for(Contact c : [select AccountId from Contact limit 200 offset 0]) {
    if(!mapContactIdLstAccount.containsKey(c.AccountId))
        mapContactIdLstAccount.put(c.AccountId, New Contact[]{});
    mapContactIdLstAccount.get(c.AccountId).add(c);
}
system.debug(system.now() + ' ' + system.now().millisecond());

232 Milliseconds - From Eric's Answer

Map<Id,List<Contact>> mapContactIdLstAccount = new Map<Id,List<Contact>>();
system.debug(system.now() + ' ' + system.now().millisecond());
for(Contact c : [select AccountId from Contact limit 200 offset 0]) {
    if(mapContactIdLstAccount.containsKey(c.AccountId)) {
        mapContactIdLstAccount.get(c.AccountId).add(c);
    } else {
        mapContactIdLstAccount.put(c.AccountId, new Contact[]{c});
    }
}
system.debug(system.now() + ' ' + system.now().millisecond());

156 Milliseconds - From SFDCFox Answer

Map<Id,List<Contact>> mapContactIdLstAccount = new Map<Id,List<Contact>>();
system.debug(system.now() + ' ' + system.now().millisecond());
for (Contact c : [select AccountId from Contact limit 200 offset 0]) {
    List<Contact> lstContact = mapContactIdLstAccount.get(c.AccountId);
    if (lstContact == null) {
        lstContact = new List<Contact>();
        mapContactIdLstAccount.put(c.AccountId, lstContact);
    }
    lstContact.add(c);
}
system.debug(system.now() + ' ' + system.now().millisecond());

148 Milliseconds - From Keith Answer

Looking at the results, selecting Keith's answer as Accepted Answer. Thank you all for your time.

0
4

In terms of CPU utilization, the preferred form is the first one:

for(a b: c) {
    if(d.containsKey(b.e)) {
        d.get(b.e).add(b);
    } else {
        d.put(b.e, new f[]{b});
    }
}

Other forms of this code, such as the second form, are easier to get code coverage for, but have a distinct performance penalty. You should prefer to use the optimal form for performance unless it's used only rarely.

The reason why is because a = new b[0]; a.add(c); is slower than new b[] {c};, and, accordingly, you'll get better performance when you use the exact form I recommended over other forms. Of course, this really only matters if you're talking about processing hundreds of records in a list (e.g. trigger context). If you're simply mapping a few records, the performance penalty doesn't matter.

4

The second one performs additional unnecessary steps that could increase CPU time....

This is what I would do, simplifying the process even further (Although as sfdcfox points out eats a few extra milliseconds)

for(SObject so : listSObject) {
    if(!mapSO.containsKey(so.ParentId))
        mapSO.put(so.PartentId, New sObject[]{});

        mapSo.get(so.ParentId).add(so);
}

Now there are times where you would want to pull the list out first, for example if you are going to be doing modifications to the list itself then you do not always want to execute the map.get() methods as you could do it once, perform the updates, then put it back. It saves cpu cycles for constantly executing the get() method...

While it seems trivial, with large data sets, the time really adds up. This is where the DEV console comes into play by being able to monitor the execution time and where it is spent.

4
  • Unfortunately, this form is slower than the optimal CPU usage form I described in my answer.
    – sfdcfox
    Commented Aug 13, 2015 at 3:37
  • 2
    Actually, it's because changing new x[] {}; y.add(z); to new x[] {z}; results in a performance increase. Although, using the ! operator also increases processor usage.
    – sfdcfox
    Commented Aug 13, 2015 at 3:43
  • For what it's worth, I used this syntax back when "line count" was a governor limit, because it had better performance. Now that "CPU time" is king, we've had to re-learn common constructs to optimize performance.
    – sfdcfox
    Commented Aug 13, 2015 at 3:45
  • Yea, as soon as you posted I thought about it and realized what you were saying.....Just like passing a string to a method to check if it is equal to something takes more CPU time than just writing the line in the local method. The construction of the method to call it (or however you say it) during a batch process for each record eats up a lot of time. Takes more lines of code to check the strings but when you are talking 1000's of records the .03 seconds per record adds up
    – Eric
    Commented Aug 13, 2015 at 3:51

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .