[SalesForce] Apex: Duplicates in list when adding set to empty list

I came across a weird behaviour of which I don't know why it happens. Maybe you guys can help me:

Setup:

I have a list of records, over which I iterate (list name: fieldAutomations).
Each iteration searches for a record in a Map, creates a new SObject-record and updates a field in this record.
This record is then saved in a SObject-Set called 'updateFields'.

It is possible that different iterations of fieldAutomations find the same record in the map.

After all records have been iterated through, the 'updateFields'-Set is inserted into a list which is then eventually used to update the records.

Question 1:
When I have a record in my SObject-Set 'updateFields' where the value in field A has changed, and the same record is written to the 'updatedFields'-Set with a changed value in field B, it overwrites the first object.
However, in the debugs it shows that both fields (A and B) were changed to the new values.
Why does it do that?

Question 2:
After the iteration through the records, the set is inserted into a newly created list which is then used to update the records.
However, when there are two changes in the same record but only one item is in the set (because duplicates are not allowed), and I insert the set into the new list, There are two records with the same Id in the list. Which leads to an error on update.

Here is the (important part of the) code:

Map<String,SObject> objects = new Map<String,SObject>();
    Set<SObject> updateRecords = new Set<SObject>();

    for (cs_Field_Automation__c fieldAutomation : fieldAutomations) {
        System.debug('updateRecords (first): ' + updateRecords);
        // get record from objects map
        SObject record = objects.get(fieldAutomation.Object_Name__c);

        // Some logic happens here

        // update field 

        record.put(fieldAutomation.Field_Name__c, fieldAutomation.New_Value__c);

        updateRecords.add(record);

    }

    if (updateRecords.isEmpty() == false) {
        System.debug('updateRecords (Set): ' + updateRecords); // here, only one item is in the set
        List<SObject> updateRecordsList = new List<SObject>();
        System.debug('updateRecordsList (List): ' + updateRecordsList); // list is empty
        updateRecordsList.addAll(updateRecords);
        System.debug('updateRecordsList (after insert of set): ' + updateRecordsList); // lsit has two items with the same Id
        return updateRecordsList;
    } else {
        return null;
    }

This is part of the debug log that shows how there are two items in the list that was filled with te set that only had one item:

|USER_DEBUG|[903]|DEBUG|updateRecords (Set): {Account:{Id=0011t00000x3PiaAAE, Name=Testaccount, Website=null}, Opportunity:{Id=0061t00000JCcfEAAT, Name=Test Opportunity, StageName=Closed, CloseDate=2019-09-24 00:00:00, AccountId=0011t00000x3PiaAAE}}
|USER_DEBUG|[905]|DEBUG|updateRecordsList (List): ()
|USER_DEBUG|[907]|DEBUG|updateRecordsList (after insert of set): (Opportunity:{Id=0061t00000JCcfEAAT, Name=Test Opportunity, StageName=Closed, CloseDate=2019-09-24 00:00:00, AccountId=0011t00000x3PiaAAE}, Opportunity:{Id=0061t00000JCcfEAAT, Name=Test Opportunity, StageName=Closed, CloseDate=2019-09-24 00:00:00, AccountId=0011t00000x3PiaAAE}, Account:{Id=0011t00000x3PiaAAE, Name=Testaccount, Website=null})

Best Answer

You are getting this error because you used Set<SObject> without fully understanding the mechanics of it, which is why I usually recommend against this structure entirely. Instead, you should use Map<Id, SObject>. For the former data structure, you only get deduplication if every field matches. For the latter data structure, you get deduplication if and only if Id matches.

Related Topic