Are sObjects supported as the keys for Maps?
I thought they were, but it appears that any subsequent changes to the key sObject break the mapping to the value.
This might be better shown with an anonymous apex example (API v27.0).
Map<OpportunityLineItem, string> testMap = new Map<OpportunityLineItem, string>(); OpportunityLineItem testOli = new OpportunityLineItem(); testOli.Description = 'foo'; testMap.put(testOli, 'bar'); System.assert(testMap.containsKey(testOli)); string mapValue = testMap.get(testOli); System.assertEquals('bar', mapValue); // After this the OLI will no longer be a key in the map. testOli.OpportunityId = '0064000000RB2eJ'; System.assert(testMap.containsKey(testOli), 'Expected to still contain OLI'); string mapValue2 = testMap.get(testOli); System.assertEquals('bar', mapValue2, 'OLI instance still expected to map to string bar');
This fails for me with:
System.AssertException: Assertion Failed: Expected to still contain OLI
Is this the expected behaviour?
Yes, that is the expected behavior. Behind the scenes, Salesforce is hashing the sObject. By changing field values, you change the hash. A better practice is to use the sObject ID as the key. This blog post explains some more: https://appirio.com/tech-blog/considerations-using-sobjects-in-sets-and-map-keys-in-apex
When planning to use an SObject as the key for a Map, be aware that it can sometimes behavein an unexpected fashion. If you are expecting to encounter a scenario where the SObject can be mutated (even indirectly, as done by an insert) once set as a key for this Map, you will lose the
reference to the value for this Map for the SObject key that was mutated.