Take the 2-minute tour ×
Salesforce Stack Exchange is a question and answer site for Salesforce administrators, implementation experts, developers and anybody in-between. It's 100% free, no registration required.

I have this below code where I create a map with sobject as key and integer as value. I added 2 account sobject keys. Size is 2, and I later changed one of its names to the other one so that now they are duplicated.

Ideally map since it's a reference to sobject, changes should be reflected and map size should shrink to one. In my code, it does only if i do a system.debug of map before i calculate size/assert. It's the same behavior for set as well. Why is that so?

map<account,integer> mymap = new map<account,integer>();
account a = new account(name='apple');
mymap.put(a,1);
system.assertequals(mymap.size(),1);
mymap.put(new account(name='orange'),1);
system.assertequals(mymap.size(),2);
a.name='orange';
system.debug(mymap);//if i remove this my assertion will fail and size will be displayed as 2.if i add this statement before ,size is 1 and assertion succeeds
system.debug(mymap.size());
system.assertequals(mymap.size(),1);
share|improve this question

4 Answers 4

up vote 9 down vote accepted

I've come across a similar issue before in Using an sObject as a Map key.

From Map Considerations:

Uniqueness of map keys of user-defined types is determined by the equals and hashCode methods, which you provide in your classes. Uniqueness of keys of all other non-primitive types, such as sObject keys, is determined by comparing the objects’ field values.

And sObject Map Considerations

Be cautious when using sObjects as map keys. Key matching for sObjects is based on the comparison of all sObject field values. If one or more field values change after adding an sObject to the map, attempting to retrieve this sObject from the map returns null. This is because the modified sObject isn’t found in the map due to different field values. This can occur if you explicitly change a field on the sObject, or if the sObject fields are implicitly changed by the system; for example, after inserting an sObject, the sObject variable has the ID field autofilled. Attempting to fetch this Object from a map to which it was added before the insert operation won’t yield the map entry, as shown in this example.

Something interesting I found. As you say, the assertions pass when I run your sample code as anonymous apex with the Apex Code logging level set to DEBUG. However, with exactly the same code, if you set the Apex Code logging level to FINEST it will fail the assertion!

share|improve this answer
    
yes you are correct..it fails assertions when log level is changed ..Interesting :).and thanks –  sfdc99999 Jun 27 at 9:20
1  
Nitpick: your first documentation quote is untrue for apex classes that have defined hashCode and equals methods, but is certainly the case for SObjects. –  ca_peterson Jun 28 at 20:12
    
@ca_peterson Good catch see the docs had been updated to include this since the quote was originally taken. I've updated the details here to be more accurate. –  Daniel Ballinger Jun 29 at 9:15

This reminded me of an exact section in Adavanced Apex Programming by Dan Appleman. The section blatantly was called Avoid Using Objects as Keys.

Apex uses a hash of the field values as the internal value to use when searcing for the object in the map or set. Changing a field on an object changes this hash value, causing the same object to appear as two distinct objects when used as keys.

Given that one of the main purposes of maps and sets when used with objects is to hold them while they are being modified, using objects as keys or in sets is a sure way to create subtle and hard to find bugs.

share|improve this answer
    
This, and a number of other statements I've heard from Dan Appleman makes me distrust his books. There are perfectly valid reasons for using objects as keys. Any advanced programmer would jump right in and use them wisely, while a novice would jump right in and cause subtle bugs mentioned by Dan. Please tell me this quote isn't from his "Advanced Apex Code" book. –  sfdcfox Jul 1 at 17:18

There are a lot of odd things that go on with the collections in order to support the various legacy interaction with the rest of platform and this is one of them. Salesforce R&D has a bug filed with them with no clear ETA.

share|improve this answer

Here is a Known issue filed against the issue: https://success.salesforce.com/issues_view?id=a1p30000000T4hVAAS

share|improve this answer
    
This isn't really a bug, though. It's by design. Keys are meant to be immutable, and if you violate that design, you get what you ask for. If they "fix" it to use memory addresses, it will break code in many places, including my own. –  sfdcfox Jul 1 at 17:58

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.