Can sObjects Be Used as Keys in an Apex Map?

Can sObjects Be Used as Keys in an Apex Map?

On July 6, 2025, Posted by , In Apex,Salesforce, By ,, , With Comments Off on Can sObjects Be Used as Keys in an Apex Map?
Can sObjects Be Used as Keys in an Apex Map

Question

Are sObjects supported as keys in an Apex Map? I initially thought they were, but I observed that modifying an sObject after inserting it into the map seems to break the mapping.

Here’s an example in anonymous Apex (API v27.0) demonstrating the issue:

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);

// Modifying a field after insertion
testOli.OpportunityId = '0064000000RB2eJ';

// The key seems to be lost after modification
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 results in the following error:

System.AssertException: Assertion Failed: Expected to still contain OLI

Is this the expected behavior in Apex? If so, why does this happen, and what is the best way to work around it?

Answer

Yes, this is the expected behavior in Apex. The reason is that sObjects are not immutable and their hash code changes whenever a field value is modified. Since Apex Maps use hash codes to track keys, modifying an sObject breaks the reference in the map, making it impossible to retrieve the original mapping.

To work around this limitation, one approach is to avoid modifying sObjects used as keys. However, in many cases, this is not practical. A better approach is to wrap the sObject inside a container class. This ensures that the reference remains stable even if the underlying sObject is modified.

Here is an example of how to implement a wrapper class to use sObjects as keys:

public class AccountContainer {
    public Account accountSObject;
    public AccountContainer(Account accountSObject) { 
        this.accountSObject = accountSObject; 
    }
}

public class ContactContainer {
    public Contact contactSObject;
    public ContactContainer(Contact contactSObject) { 
        this.contactSObject = contactSObject; 
    }
}

Account a1 = new Account(Name = 'Account 1');
AccountContainer ac1 = new AccountContainer(a1);
Contact c1 = new Contact(LastName = 'Contact 1');
ContactContainer cc1 = new ContactContainer(c1);

Map<ContactContainer, AccountContainer> mapAccountByContact_Container = new Map<ContactContainer, AccountContainer>{ cc1 => ac1 };
Map<Contact, Account> mapAccountByContact_SObj = new Map<Contact, Account>{ c1 => a1 };

AccountContainer beforeChanging_Container = mapAccountByContact_Container.get(cc1);
Account beforeChanging_SObject = mapAccountByContact_SObj.get(c1);

System.debug(beforeChanging_SObject === a1);        // true
System.debug(beforeChanging_Container === ac1);     // true

c1.LastName = 'Contact 1 changed';

AccountContainer afterChanging_Container = mapAccountByContact_Container.get(cc1);
Account afterChanging_SObject = mapAccountByContact_SObj.get(c1);

System.debug(beforeChanging_SObject === afterChanging_SObject);         // false
System.debug(beforeChanging_Container === afterChanging_Container);     // true

In this approach, even if the sObject inside the container is modified, the reference to the wrapper object remains unchanged, ensuring that the map lookup still works.

While this method works well for most cases, there are potential drawbacks. The wrapper class does not override equals() and hashCode(), which could lead to unexpected behavior in some situations, such as when comparing two different instances of the same sObject. If necessary, you may need to override these methods to ensure correct key matching.

Another alternative is to use the sObject’s Id field as the key instead of the sObject itself. Since an sObject’s Id remains constant, using it as a key avoids issues with hash code changes:

Map<Id, Account> accountMap = new Map<Id, Account>();
Account acc = new Account(Id = '0014000001ABC123', Name = 'Test Account');
accountMap.put(acc.Id, acc);

// Modify account fields
acc.Name = 'Updated Name';

// Lookup still works because the Id has not changed
System.assert(accountMap.containsKey(acc.Id));

In this example, a Map<Id, Account> is used to store an Account record, with the Id field serving as the key. Since the Id of a Salesforce sObject remains constant once assigned, it provides a stable key that is unaffected by changes to other fields.

After inserting the Account object into the map, the Name field is modified. Despite this change, the lookup using acc.Id still works because the Id has not been altered. This approach ensures that the key remains valid, unlike when using the sObject itself as a key, which would break the mapping if any field were modified. Using Id as a key is a best practice when working with persistent records, as it guarantees a reliable reference for map lookups.

Enroll for Career-Building Salesforce Training with 100% Money Back Guarantee

Our Salesforce course is carefully designed to give you a thorough understanding of the Salesforce platform, equipping you with the essential skills to succeed in the CRM industry. The program covers core modules like Salesforce Admin, Developer, and AI, combining theoretical concepts with hands-on practice. By engaging in real-world projects and interactive assignments, you’ll develop the expertise to solve business challenges using Salesforce solutions. Our expert instructors ensure you gain both technical proficiency and industry-relevant knowledge to excel in the Salesforce ecosystem.

In addition to technical training, our Salesforce Training in Dallas provides personalized mentorship, certification support, and interview preparation to enhance your career prospects. You’ll get access to extensive study resources, hands-on project experience, and dedicated guidance throughout the course. By completion, you’ll be well-prepared for certification exams and possess the problem-solving skills that employers look for. Begin your Salesforce journey today and unlock exciting career opportunities. Sign up for a Free Demo now!

Comments are closed.