2

I've created a class of a custom datatype:

public class KPI {
    public String name { get; set; }
    public Decimal target { get; set; }
    public Decimal actual { get; set; }
    public Decimal percentage { get; set; }

    public KPI (String name, Decimal target, Decimal actual) {
        this.name = name;
        this.target = target;
        this.actual = actual;
        this.percentage = (target > 0 && actual > 0) ? (actual / target) * 100 : 0;
    }
}

I have a function that is generating a List of this empty data type:

public static Map<Id, List<KPI>> CreateMapUserIdToKPIList() {
    List<KPI> kpiList = new List<KPI>();
    for (KPI_Setting__c kpiSetting : kpiSettingList) {
        kpiList.add(new KPI(kpiSetting.Name__c, 0.00, 0.00));
    }

    Map<Id, List<KPI>> mapUserIdToKPIList = new Map<Id, List<KPI>>();
    for (Id userId : userMap.keySet()) {
        mapUserIdToKPIList.put(userId, kpiList.clone());
    }

    return mapUserIdToKPIList;
}

However, when I iterate through the Map and the mapped List any changes made to one List is effecting the all List collections in the map. I know this is because the List<KPI> is referencing the original, empty List.

When I do the following, changes made to one List doesn't affect them all:

public static Map<Id, List<KPI>> CreateMapUserIdToKPIList() {
    Map<Id, List<KPI>> mapUserIdToKPIList = new Map<Id, List<KPI>>();
    for (Id userId : userMap.keySet()) {
        List<KPI> kpiList = new List<KPI>();
        for (KPI_Setting__c kpiSetting : kpiSettingList) {
            kpiList.add(new KPI(kpiSetting.Name__c, 0.00, 0.00));
        }

        mapUserIdToKPIList.put(userId, kpiList);
    }

    return mapUserIdToKPIList;
}

However, this seems really inefficient to loop through each user and in that loop, loop through another collection generating the same values that have been generated before.

I've tried, instead of using kpiList.clone() things like new List<KPI>(kpiList) but that too references the initial list. I can't use deepClone() as I'm getting an error stating deepClone can only be used on sObject types.

Is there a way to "deepClone()" a custom datatype List?

1

1 Answer 1

2

As Tushar mentioned, you can use JSON.serialize/JSON.deserialize to create deep clones. However, the problem is that the JSON methods have relatively poor performance, so you're not likely to gain any performance benefit over doing the inefficient way of using a nested for loop. That is, unfortunately, the fastest way to do so. You can shave off a bit of time by allocating all the heap at once for the list:

Integer listSize = kpiSettings.size();
for (Id userId : userMap.keySet()) {
  KPI[] kpiList = new KPI[listSize];
  for(Integer i = 0; i < listSize; i++) {
    kpiList[i] = new KPI(kpiSettingsList[i].Name__c, 0.0, 0.0);
  }
  mapUserIdToKPIList.put(userId, kpiList);
}

This is at least marginally more efficient than the method you have now.

1
  • Thank you for this. I'm disappointed we can't clone a custom datatype List in a simpler way, but thanks for confirming I'm not going completely mad. I'll implement this as you've suggested! Thanks again!
    – Dan Jones
    Jun 28, 2018 at 13:24

You must log in to answer this question.

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