ship
SalesForce Simplified

Your Go-To Resource for Streamlined Solutions and Expert Guidance

mountains
Empower Your Business
Dive deep into the world of CRM excellence, where innovation meets practicality, and transform your Salesforce experience with Forceshark's comprehensive resources

Unlocking the Power of Custom Wrapper Classes in Apex Maps

When working with Apex in Salesforce, Maps (key-value pairs) are incredibly useful for storing and retrieving data efficiently. But have you ever tried using a custom wrapper class as a key in an Apex Map? That’s where things get really interesting. Let’s break it down.

Benefits of Using Custom Wrapper Classes with Apex Maps

Why use a wrapper class as a key instead of a standard type like String or Id? Here’s why:

More Complex Keys Instead of just an Id, you can use multiple fields combined as a key. Imagine needing to track records based on both AccountId and ContactId—a wrapper lets you do that.
Increased Readability Using a descriptive wrapper class makes your code clearer and easier to maintain.
Avoids Key Collisions Sometimes, using just an Id isn't enough. A wrapper ensures uniqueness based on multiple attributes.
Better Organization Grouping related fields together in a wrapper keeps your data structured logically.

Creating a Simple Wrapper Class

Let’s start with a basic wrapper class. Suppose we need to map Salesforce Contacts to their corresponding Accounts using a combination of AccountId and ContactId.

public class AccountContactKey {
    public Id accountId;
    public Id contactId;

    public AccountContactKey(Id accountId, Id contactId) {
        this.accountId = accountId;
        this.contactId = contactId;
    }

    public Boolean equals(Object obj) {
        if (obj instanceof AccountContactKey) {
            AccountContactKey other = (AccountContactKey) obj;
            return this.accountId == other.accountId && this.contactId == other.contactId;
        }
        return false;
    }

    public Integer hashCode() {
        return (String.valueOf(accountId) + String.valueOf(contactId)).hashCode();
    }
}

What’s Going On Here?

  • Constructor – Initializes the wrapper with accountId and contactId.
  • equals() – Ensures two objects with the same accountId and contactId are considered equal.
  • hashCode() – Generates a unique hash based on accountId and contactId to work correctly as a key in a Map.

Using Wrapper Classes as Keys in Apex Maps

Now that we have our wrapper class, let’s see it in action.

Example: Mapping Contacts to Their Parent Accounts
Map<AccountContactKey, Contact> contactMap = new Map<AccountContactKey, Contact>();

List<Contact> contacts = [SELECT Id, FirstName, LastName, AccountId FROM Contact WHERE AccountId != NULL];

for (Contact con : contacts) {
    AccountContactKey key = new AccountContactKey(con.AccountId, con.Id);
    contactMap.put(key, con);
}

// Retrieve a specific contact
Id testAccountId = '001XYZ000123'; 
Id testContactId = '003XYZ000123'; 
AccountContactKey searchKey = new AccountContactKey(testAccountId, testContactId);

if (contactMap.containsKey(searchKey)) {
    Contact retrievedContact = contactMap.get(searchKey);
    System.debug('Contact found: ' + retrievedContact.FirstName + ' ' + retrievedContact.LastName);
}

What’s Happening?

  • We query all Contacts with an associated AccountId.
  • We create an AccountContactKey for each contact and use it as a key in the contactMap.
  • Later, we use another instance of AccountContactKey to look up a specific contact.

Real-World Use Case: Tracking Multi-Key Relationships

Imagine needing to map Opportunities based on both AccountId and StageName. Instead of using a complex nested map (Map<Id, Map<String, Opportunity>>), a wrapper makes it simpler:

public class AccountStageKey {
    public Id accountId;
    public String stageName;

    public AccountStageKey(Id accountId, String stageName) {
        this.accountId = accountId;
        this.stageName = stageName;
    }

    public Boolean equals(Object obj) {
        if (obj instanceof AccountStageKey) {
            AccountStageKey other = (AccountStageKey) obj;
            return this.accountId == other.accountId && this.stageName == other.stageName;
        }
        return false;
    }

    public Integer hashCode() {
        return (String.valueOf(accountId) + stageName).hashCode();
    }
}
// Usage Example
Map<AccountStageKey, List<Opportunity>> oppMap = new Map<AccountStageKey, List<Opportunity>>();
List<Opportunity> opps = [SELECT Id, Name, AccountId, StageName FROM Opportunity];

for (Opportunity opp : opps) {
    AccountStageKey key = new AccountStageKey(opp.AccountId, opp.StageName);
    if (!oppMap.containsKey(key)) {
        oppMap.put(key, new List<Opportunity>());
    }
    oppMap.get(key).add(opp);
}

Conclusion

Using custom wrapper classes as keys in Apex Maps is a game-changer. It allows for complex multi-field keys, improves code readability, and prevents collisions when working with related records. Whether you're mapping opportunities by stage, tracking unique relationships, or ensuring efficient bulk processing, wrapper keys make your life easier. Try them out in your next project, and you'll wonder how you ever managed without them!