100% Free Forever
AI-Powered Learning
Industry Expert Content
Certificates & Badges
Learn At Your Own Pace
Programming

Lists, Sets, and Maps

Master Apex's three core collection types — ordered Lists, unique Sets, and key-value Maps — and when to use each for bulk-safe, governor-limit-friendly code.

Control Flow & CollectionsBeginner10 min readJul 10, 2026
Analogies

List: an ordered, indexable collection

A List<T> in Apex is an ordered collection that allows duplicate values and is accessed by zero-based index, declared as List<Account> accts = new List<Account>();. Lists are the most common return type for SOQL queries — every SELECT statement without a LIMIT 1 and single-row assignment returns a List<sObject> — and they support methods like add(), remove(), size(), isEmpty(), and sort().

🏏

Cricket analogy: A batting order is a List: it's strictly ordered from opener at index 0 down to number 11, and the same player's name could theoretically repeat if used as a placeholder, unlike a Set which forbids duplicates.

Set: unique, unordered membership

A Set<T> in Apex guarantees uniqueness and has no defined order (except Set<sObject>, which is rare and discouraged). Sets are the natural choice for collecting distinct Ids — for example, gathering unique AccountIds from a List<Contact> before a WHERE Id IN :accountIds SOQL query — because adding a duplicate value to a Set silently does nothing rather than throwing an error.

🏏

Cricket analogy: The set of countries that have won a Cricket World Cup is a Set: each nation appears exactly once regardless of how many times they've actually won, membership is what matters, not order or count.

apex
// Classic pattern: collect unique parent Ids from a child list, then bulk-query parents
List<Contact> contacts = [SELECT Id, AccountId, Email FROM Contact WHERE Email != null];

Set<Id> accountIds = new Set<Id>();
for (Contact c : contacts) {
    if (c.AccountId != null) {
        accountIds.add(c.AccountId); // duplicates are silently ignored
    }
}

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

List<Contact> contactsToUpdate = new List<Contact>();
for (Contact c : contacts) {
    Account relatedAcct = accountsById.get(c.AccountId);
    if (relatedAcct != null && relatedAcct.Industry == 'Technology') {
        c.Description = 'Tech account contact';
        contactsToUpdate.add(c);
    }
}
update contactsToUpdate;

Map: key-value lookups without repeated queries

A Map<K, V> stores key-value pairs with unique keys, declared as Map<Id, Account> acctsById = new Map<Id, Account>();. The most powerful Apex-specific feature is that any Map<Id, sObject> can be constructed directly from a List<sObject> or SOQL query result, automatically keying every record by its Id — this is the single most common way to turn a query result into an O(1) lookup structure instead of nesting a second query inside a loop.

🏏

Cricket analogy: A scorecard app's player-stats lookup keyed by player ID is a Map: given a player ID you get their stats instantly, without scanning every entry in the squad list.

The pattern shown above — query children, collect a Set of parent Ids, query parents into a Map<Id, sObject>, then loop once using map.get(id) — is the canonical way to avoid nested SOQL queries inside a loop, which is illegal from a governor-limit perspective and would throw a runtime error after 100 queries. Maps also support keySet(), values(), containsKey(), and can use composite keys like a concatenated String when a single field isn't unique enough.

🏏

Cricket analogy: A stadium's turnstile system doesn't re-verify a ticket's authenticity against the central database on every single re-entry; it caches the validated ticket ID locally, the same way a Map avoids re-querying the same record repeatedly.

Constructing a Map directly from a query, e.g. new Map<Id, Account>([SELECT Id, Name FROM Account]), is idiomatic Apex and automatically deduplicates by Id if the same record somehow appears twice in the result set.

Nesting a SOQL query inside a for loop to look up related records — instead of pre-building a Map<Id, sObject> — is one of the fastest ways to hit 'System.LimitException: Too many SOQL queries: 101' in a bulk operation of more than 100 records.

  • List<T> is ordered, indexable by position, and allows duplicate values — the default return type for SOQL queries.
  • Set<T> guarantees unique membership with no defined order; ideal for collecting distinct Ids before a bulk query.
  • Map<K,V> stores unique keys mapped to values and can be constructed directly from a List<sObject> or query, auto-keyed by Id.
  • The canonical bulk pattern: collect a Set of parent Ids, query parents into a Map<Id,sObject>, then use map.get() inside a single loop.
  • Adding a duplicate to a Set is a silent no-op; adding a duplicate key to a Map overwrites the previous value.
  • Avoid nested SOQL inside loops entirely — Maps built before the loop replace the need for repeated queries.
  • Composite String keys let you build Maps keyed by more than one field when a single field isn't unique.

Practice what you learned

Was this page helpful?

Topics covered

#Programming#ApexSalesforceStudyNotes#ListsSetsAndMaps#Lists#Sets#Maps#List#DataStructures#StudyNotes#SkillVeris