2

I have an apex class which updates an Account variable "YearlySales__c" based off of opportunities(from first of the year till now)(it should be run once a year). In this class I use a set to contain all the accounts that need to be updated(if their yearlySales__c variable has changed).

There are two times when the yearlySales__c will change(thus the account will need to be updated). These are at the beginning of the class I set all YearlySales__c to 0(to account to when the year changes and everything will be reset) and put all the changed accounts in the "updateSet" set, and when the class iterates through the opportunities and adds the amounts into the appropriate yearlySales__c(and adds the changed account to the "updateSet" set).

At the end of the class I must update the records of these accounts so I put the updateSet into a List(via .addAll). The problem is that the list has duplicate ID's which is not allowed. When I debugged this I found that the set I'm using simply does not "show" duplicates. However, the duplicates are still in the set and get added to the List with the correct elements. Why does this happen and can this monster be stopped? The code to clarify is as follows:

To Zero out YearlySales__c

for(Account acc: accountIdMap.values())
        {            
            if(acc.YearlySum__c != 0)
            {
               acc.YearlySum__c = 0;
               updateSet.add(acc);             
            }            
        }           

When adding to YearlySales__c

if(account.YearlySum__c != 0)
            {
                account.YearlySum__c += opp.Amount;                    
                updateSet.add(account);                    
            }            
            else
            {                
                account.YearlySum__c = opp.Amount;                    
                updateSet.add(account);               
            }                         

The transfer(from Set to List)

if(updateSet != null)
        {
            updateList.addAll(updateSet);            
            update updateList;
            return updateList.size();
        }

The debug log showing the heresy

Set: 6, {Account:{Id=001e000000fBeVQAA0, YearlySum__c=100.00, Name=Test Account}, Account:{Id=001e000000fBeVRAA0, Name=Test Account2, YearlySum__c=200.00}, Account:{Id=001e000000fBeVPAA0, YearlySum__c=0, Name=Test Poor Account}}

List: 6, (Account:{Id=001e000000fBeVPAA0, YearlySum__c=0, Name=Test Poor Account}, Account:{Id=001e000000fBeVQAA0, YearlySum__c=100.00, Name=Test Account}, Account:{Id=001e000000fBeVRAA0, Name=Test Account2, YearlySum__c=200.00}, Account:{Id=001e000000fBeVQAA0, YearlySum__c=100.00, Name=Test Account}, Account:{Id=001e000000fBeVRAA0, Name=Test Account2, YearlySum__c=200.00}, Account:{Id=001e000000fBeVRAA0, Name=Test Account2, YearlySum__c=200.00})

Notice if you would that the dubs in the List are identical in all fields.

1
  • 1
    As an aside, if(updateset != null) is superfluous code. It will never be null.
    – sfdcfox
    Commented Aug 5, 2015 at 19:27

2 Answers 2

0

It appears to me that here's what's happening:

When you add the accounts to your set the first time, you're adding them from a map you've created using the method from below that just pulls values from a map:

for(Account acc: accountIdMap.values())
        {            
            if(acc.YearlySum__c != 0)

When you do it the 2nd time, you reference the account a different way as below:

if(account.YearlySum__c != 0)
            {
                account.YearlySum__c += opp.Amount;

Apex sees this as two different accounts because of where the account ID's came from. It has to do with hash methods is the best way I can explain it.

If you add a test to see if the set contains the Id first, it wouldn't add the Id an additional time. That would solve your issue.

2
  • Thanks, I ended up using a map anyways, but was still curious about this. I may switch my code back to a set now.
    – smurph006
    Commented Aug 5, 2015 at 20:17
  • This answer isn't right. Salesforce doesn't care where the ID values come from. It cares about the hash of an object. SObjects consider their entire contents, not just their ID.
    – sfdcfox
    Commented Aug 5, 2015 at 22:51
1

Modifying a record that is currently stored in a Set will cause the hash code for that record to change. You can observe the same effect using this very simple code:

Account a = new Account();
Set<Account> b = new Set<Account>();
b.add(a);
a.Name = 'Updated Name';
// System.assert(b.contains(a)) will fail
b.add(a);
// System.assertEquals(1, b.size()) will fail

Instead, use a unique Id, such as an Id or a GUID or something.

1
  • Thanks for the explanation. The combination of both of the answers has cleared it up perfectly.
    – smurph006
    Commented Aug 5, 2015 at 20:18

You must log in to answer this question.

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