How to avoid instantiating object inside a loop?

There are many scenarios where I have to create records of polymorphic objects using apex. For e.g. creating a Task record or a CustomObject__c record for each Case created. In general I do something like below in Apex:

for(case c:createdCases)
{
    task t = new task();
    t.OwnerId = somemap.get(c.Id);
    t.subject = 'Please Close Case # '+c.CaseNumber;
    t.Priority = 'High';
    t.Status = 'Open';
    t.whatId = c.Id;
    t.ActivityDate = Date.TODAY().addDays(3);
    t.Description = 'Please close this case before the due date - '+ Date.TODAY().addDays(3).format();
    taskList.add(t);

}

insert taskList;

I would love to know if task t = new task();or customObj__c obj = new customObj__c; inside a for loop is a costly affair. If yes, does it going to add up significantly to the heap size for the execution context. How can we avoid this?

Answer

Recommendation

Object instantiation is fairly cheap. However, you can make it more efficient in two ways:

  1. Set field values using name/value pairs.
  2. Don’t cache the object, just add it directly to the list.

So that would look like:

for (Case record : createdCases)
{
    tasks.add(new Task(
        OwnerId=someValue,
        Subject='Some other value',
        Priority='etc.'
    ));
}

Profiling

I did some profiling to figure out how these two factors affect CPU cost. I did ten runs of one trial of each type laid out below. Subsequent runs were much faster, so I excluded them from my results (or rather stopped running them).

TL;DR

Most of the cost you can make up is in the name/value pairs. With the removal of caching having a negligible effect on CPU consumption, that aspect seems primarily stylistic.

Tabular Format

Operation         Average    Minimum    Maximum
Empty                64.0         56         74
Efficient           477.0        432        516
Caching             482.1        438        581
Setting Fields      555.1        512        664

Empty Loop Cost

First, I profiled an empty loop so I can subtract out the operations we don’t care about. Something like:

final Integer COUNT = 100;
List<Account> records = [SELECT OwnerId FROM Account LIMIT :COUNT];
Long start = Datetime.now().getTime();
for (Integer i = 0; i < COUNT; i++)
{
    List<Task> tasks = new List<Task>();
    for (Account record : records) continue;
}
system.debug(Datetime.now().getTime() - start);

On average, this loop took 64ms, with a minimum run time of 56ms and a maximum run time of 74ms. That means that we can assume it costs less than 1ms to instantiate the List<Task> and iterate through the Account records a single time.

Efficient Loop Cost

Next I checked out the performance of my recommended loop refactor.

final Integer COUNT = 100;
List<Account> records = [SELECT OwnerId FROM Account LIMIT :COUNT];
Long start = Datetime.now().getTime();
for (Integer i = 0; i < COUNT; i++)
{
    List<Task> tasks = new List<Task>();
    for (Account record : records)
        tasks.add(new Task(
            OwnerId=record.OwnerId, WhatId=record.Id
        ));
}
system.debug(Datetime.now().getTime() - start);

Average: 477ms, Minimum: 432ms, Maximum: 516ms.

Record Caching

final Integer COUNT = 100;
List<Account> records = [SELECT OwnerId FROM Account LIMIT :COUNT];
Long start = Datetime.now().getTime();
for (Integer i = 0; i < COUNT; i++)
{
    List<Task> tasks = new List<Task>();
    for (Account record : records)
    {
        Task newTask = new Task(
            OwnerId=record.OwnerId, WhatId=record.Id
        );
        tasks.add(newTask);
    }
}
system.debug(Datetime.now().getTime() - start);

Average: 482.1ms, Minimum: 438ms, Maximum: 581ms.

Setting Individual Fields Cost

final Integer COUNT = 100;
List<Account> records = [SELECT OwnerId FROM Account LIMIT :COUNT];
Long start = Datetime.now().getTime();
for (Integer i = 0; i < COUNT; i++)
{
    List<Task> tasks = new List<Task>();
    for (Account record : records)
    {
        Task newTask = new Task();
        newTask.OwnerId = record.OwnerId;
        newTask.WhatId = record.Id;
        tasks.add(newTask);
    }
}
system.debug(Datetime.now().getTime() - start);

Average: 555.1ms, Minimum: 512ms, Maximum: 664ms.

Attribution
Source : Link , Question Author : Jarvis , Answer Author : Adrian Larson

Leave a Comment