Intermediate 25 min read

Maps of sObjects

By the end of this lesson, you'll be able to:

  • Build a Map<Id, sObject> from a SOQL query result
  • Explain why this pattern avoids a second query inside a loop
  • Use a Map<Id, sObject> to look up related records efficiently

Prerequisites: "Maps"

Building a Map<Id, sObject> from a query

List<Account> accounts = [SELECT Id, Name, Industry FROM Account];
Map<Id, Account> accountsById = new Map<Id, Account>(accounts);

Id someAccountId = accounts[0].Id;
System.debug(accountsById.get(someAccountId).Name);
  1. List<Account> accounts = [SELECT Id, Name, Industry FROM Account]; — a SOQL query returning a List<Account>.
  2. Map<Id, Account> accountsById = new Map<Id, Account>(accounts); — passing a List<Account> directly into new Map<Id, Account>(...) automatically builds a map keyed by each record's Id — no manual loop needed.
  3. (blank line)
  4. Id someAccountId = accounts[0].Id; — grabs one real Id from the query results.
  5. System.debug(accountsById.get(someAccountId).Name); — looks it up instantly through the map. This is such a common pattern that Apex has built-in support for it.

The problem this pattern solves

// DON'T DO THIS — a query inside a loop
for (Opportunity opp : opportunities) {
    Account acc = [SELECT Name FROM Account WHERE Id = :opp.AccountId]; // a query per iteration!
    System.debug(acc.Name);
}
  1. // DON'T DO THIS — a query inside a loop — the label says it all.
  2. for (Opportunity opp : opportunities) { — loops over every opportunity.
  3. Account acc = [SELECT Name FROM Account WHERE Id = :opp.AccountId]; — a separate query runs for every single opportunity.
  4. System.debug(acc.Name); — uses the result.
  5. } — closes the loop.

Warning: This is exactly the "SOQL-in-a-loop" governor limit problem from Module 7's "Loops and Governor Limits" lesson — line 3 runs a fresh query on every iteration, quickly hitting the 100-query-per-transaction limit with enough opportunities and crashing with a System.LimitException.

The fix: one query, then map lookups

List<Opportunity> opportunities = [SELECT Id, AccountId FROM Opportunity];

Set<Id> accountIds = new Set<Id>();
for (Opportunity opp : opportunities) {
    accountIds.add(opp.AccountId);
}

Map<Id, Account> accountsById = new Map<Id, Account>(
    [SELECT Id, Name FROM Account WHERE Id IN :accountIds]
);

for (Opportunity opp : opportunities) {
    Account relatedAccount = accountsById.get(opp.AccountId);
    System.debug(relatedAccount.Name);
}
  1. List<Opportunity> opportunities = [SELECT Id, AccountId FROM Opportunity]; — one query, run once.
  2. (blank line)
  3. Set<Id> accountIds = new Set<Id>(); — a Set to collect unique Account Ids, deduplicating automatically (this module's first lesson).
  4. for (Opportunity opp : opportunities) { — loops over the opportunities already in memory — no querying here.
  5. accountIds.add(opp.AccountId); — collects the id, duplicates silently ignored.
  6. } — closes the loop.
  7. (blank line)
  8. Map<Id, Account> accountsById = new Map<Id, Account>( — builds a lookup map...
  9. [SELECT Id, Name FROM Account WHERE Id IN :accountIds] — ...from a second query, but only ever one, fetching exactly the needed Accounts.
  10. ); — closes the constructor call.
  11. (blank line)
  12. for (Opportunity opp : opportunities) { — loops again over the same in-memory list.
  13. Account relatedAccount = accountsById.get(opp.AccountId); — an instant map lookup, no query.
  14. System.debug(relatedAccount.Name); — uses the result.
  15. } — closes the loop.

One query collects the needed Account Ids, one query fetches those Accounts, and the Map makes every subsequent lookup instant — regardless of how many opportunities there are. This exact pattern reappears constantly in later modules on triggers and bulkification.

Exercise

Given List<Contact> contacts (each with an Id and Name), build a Map<Id, Contact> keyed by Id using the constructor shortcut.

Show hint

new Map<Id, Contact>(contacts)

Salesforce: ○ Not connected Connect
APEX

Execution Results

Run your code to see the result.

Maps of sObjects Quiz

1. Why is Map<Id, sObject> such a common pattern in real Apex code?

Log in to submit the quiz and save your score.

My Notes

Log in to keep private notes on this lesson.

Questions about this lesson

No questions yet — be the first to ask.

Log in to ask a question about this lesson.

Summary

Map<Id, sObject> is the single most common Map shape in real Apex code — it lets you look up a Salesforce record by its Id in memory, entirely avoiding a second SOQL query inside a loop.