Skip to main content

Apex Collections Demystified: The Complete Guide to Lists, Sets, and Maps

In plain words: Think of a List as an ordered checklist that allows duplicate items, a Set as a club roster where every member name must be strictly unique, and a Map as a dictionary or phonebook where you look up a definition (value) using a specific word (key).

In Salesforce development, writing efficient, bulkified Apex code depends entirely on how well you structure and manage data in memory. Collections are the core data structures in Apex used to process records in bulk, prevent governor limit violations, and optimize database transactions. Apex provides three primary collection types: Lists, Sets, and Maps.

1. Apex Lists: Ordered Collections with Duplicates

A List is an ordered collection of elements indexed by an integer starting at position 0. Lists allow duplicate elements and are the primary data structure returned by SOQL queries and accepted by DML operations.

Declaration, Initialization, and Operations:
// Declaration & Initialization
List<String> fruits = new List<String>(); // Empty list
List<String> colors = new List<String>{'Red', 'Green', 'Blue'}; // Initialized with values

// Adding elements
fruits.add('Apple');
fruits.addAll(colors);

// Accessing & Removing elements by index
String firstFruit = fruits.get(0); // or fruits[0]
Integer totalCount = fruits.size();
fruits.remove(0); // Removes the item at index 0

// SOQL query assigning directly to a List
List<Contact> contactList = [
    SELECT Id, FirstName, LastName, Email 
    FROM Contact 
    WITH USER_MODE 
    LIMIT 10
];

2. Apex Sets: Unordered Collections of Unique Elements

A Set is an unordered collection of distinct elements. Sets do not allow duplicates and provide fast, constant-time membership lookups via the contains() method. Sets are essential for collecting unique Record IDs to filter SOQL queries efficiently.

Declaration, Deduplication, and Membership Checks:
// Declaration & Initialization
Set<String> cities = new Set<String>();
Set<String> uniqueColors = new Set<String>{'Red', 'Green', 'Blue'};

// Adding elements (duplicates are automatically ignored)
cities.add('New York');
cities.add('New York'); // Set still contains only one 'New York'

// Fast membership verification
Boolean hasRed = uniqueColors.contains('Red'); // Returns true

// Real-world use case: Extracting unique Email addresses from Contacts
Set<String> uniqueEmails = new Set<String>();
for (Contact con : contactList) {
    if (String.isNotBlank(con.Email)) {
        uniqueEmails.add(con.Email.toLowerCase());
    }
}

3. Apex Maps: Key-Value Associations for Fast Lookups

A Map is a collection of key-value pairs where each key is strictly unique and points to exactly one value. Maps eliminate nested loops in Apex, allowing developers to correlate parent-child records or look up reference values in O(1) time complexity.

Declaration, Key-Value Lookups, and Grouping:
// Declaration & Basic Operations
Map<String, String> countryCodes = new Map<String, String>{
    'US' => 'United States',
    'CA' => 'Canada'
};
countryCodes.put('IN', 'India');
String country = countryCodes.get('US'); // Returns 'United States'

// Constructing a Map directly from a SOQL Query
Map<Id, Account> accountMap = new Map<Id, Account>([
    SELECT Id, Name, Industry 
    FROM Account 
    WITH USER_MODE 
    LIMIT 100
]);

// Grouping multiple Child records (Opportunities) under a Parent Key (AccountId)
Map<Id, List<Opportunity>> accountToOppsMap = new Map<Id, List<Opportunity>>();
for (Opportunity opp : [SELECT Id, Name, Amount, AccountId FROM Opportunity WHERE AccountId != null LIMIT 500]) {
    if (!accountToOppsMap.containsKey(opp.AccountId)) {
        accountToOppsMap.put(opp.AccountId, new List<Opportunity>());
    }
    accountToOppsMap.get(opp.AccountId).add(opp);
}
360 Collection Architecture Card:
  • List: Ordered, allows duplicates, accessible by zero-based index. Best for DML operations and sorted UI display.
  • Set: Unordered, strictly unique items. Best for collecting IDs for IN :idsSet SOQL filters and deduplication.
  • Map: Key-value store, unique keys, instant lookups. Best for matching related records without nested loops.
  • Heap Size Limit: All collections count against the synchronous 6 MB (or asynchronous 12 MB) Apex heap limit.

4. Bulkification Traps & Best Practices

Developer Trap: Using Nested Loops Instead of Maps
Iterating through a list inside another list loop creates O(n²) complexity, driving up CPU time and risking the Apex CPU time limit exceeded error. Always populate a Map first, then retrieve records with a single lookup using map.get(key).
Core Rule: Use Sets to collect foreign keys from Trigger context records, query the database once using the Set filter, and store query results in a Map for instant access.
  • Direct Query Initialization: Use the built-in Map constructor new Map<Id, sObject>([SOQL]) to populate an ID-to-Record map in a single line of code.
  • Null Safety on Map Retrieval: Always verify that a key exists using containsKey() before calling methods on the returned value to avoid NullPointerException errors.
  • Case Sensitivity Consideration: Apex Set and Map keys for strings are case-sensitive by default (e.g., 'Apex' vs 'apex'). For case-insensitive uniqueness, normalize strings using toLowerCase() before inserting.

Summary

Mastering Lists, Sets, and Maps is the foundation of building robust, production-ready Salesforce solutions. By leveraging Lists for DML, Sets for deduplication, and Maps for high-speed record linking, you can write clean, governor-limit-friendly Apex that scales effortlessly across large data volumes.