Study guide

6.2 Lists, Sets & Maps

Salesforce Certified Platform Developer I Exam

Track your progress as you read

Finished sections get a check mark.

1 Introduction & Exam Overview

1.1 Current Exam Facts 1.2 Blueprint Weights & Study Strategy

2 Platform Fundamentals

2.1 Multi-Tenant Architecture & Shared Resources 2.2 MVC Mapping on the Lightning Platform 2.3 Declarative vs Programmatic Decisions 2.4 Agentforce for Developers: Use Cases & Limitations

3 Data Modeling & Schema

3.1 Objects, Fields & Schema Design 3.2 Relationship Types & Implications 3.3 Schema Design Impact on Apex 3.4 Data Import & Export for Development

4 Platform Data Features

4.1 Formula Fields & Roll-Up Summaries 4.2 Core CRM Objects & Standard Schema 4.3 AppExchange Extensions & Platform Boundaries

5 Declarative Process Automation

5.1 Record-Triggered Flows & Declarative Automation 5.2 Approval Processes & Business Rules 5.3 When to Use Apex vs Declarative Tools

6 Apex Language Basics

6.1 Variables, Constants & Apex Data Types 6.2 Lists, Sets & Maps 6.3 Control Flow & Exception Handling 6.4 Classes, Interfaces & OOP Patterns

7 SOQL, SOSL & DML

7.1 SOQL Query Fundamentals 7.2 SOSL Search Fundamentals 7.3 DML Operations & Bulkification 7.4 Governor Limits & Transaction Boundaries

8 Apex Triggers & Order of Execution

8.1 Trigger Structure & Context Variables 8.2 Bulk Trigger Patterns & Best Practices 8.3 Save Order of Execution 8.4 Recursion, Cascading & Transaction Control

9 Asynchronous Apex

9.1 @future Methods 9.2 Queueable Apex 9.3 Batch Apex 9.4 Schedulable Apex & Job Orchestration

10 Security, Events & Schema Access

10.1 Sharing, FLS, CRUD & Injection Prevention 10.2 Platform Events Publish & Subscribe 10.3 Programmatic Schema Access (Describe)

11 Visualforce

11.1 Visualforce Pages & Markup 11.2 Standard, Custom Controllers & Extensions 11.3 Visualforce in Lightning Experience

12 Lightning Components

12.1 Lightning Component Framework Benefits 12.2 Aura Component Structure & Resources 12.3 Lightning Web Components Fundamentals 12.4 LWC Data Access: Wire, Imperative & Events

13 UI Composition & Apex Integration Patterns

13.1 Composing UI in Lightning Apps 13.2 UI Security, Performance & Accessibility Patterns 13.3 Apex Actions for Flow, LWC, and Agentforce

14 Apex Testing

14.1 Testing Framework & Coverage Requirements 14.2 Writing Unit Tests for Triggers & Classes 14.3 Test Data Strategies & Isolation 14.4 Executing Tests & Execute Anonymous Differences

15 Debugging & Developer Tools

15.1 Debug Logs & Monitoring 15.2 Developer Console, CLI, VS Code & Workbench 15.3 Sandboxes, Scratch Orgs & Environment Strategy

16 Deployment & Release

16.1 Change Sets & Metadata Deployment 16.2 Unlocked Packages, Metadata API & Salesforce DX 16.3 Release Readiness & Credential Maintenance

6.2 Lists, Sets & Maps

Key Takeaways Explain This Topic AI

List is ordered and allows duplicates—use for query results and sequence-sensitive work

Set stores unique elements—use to deduplicate IDs and test membership efficiently

Map holds key→value pairs; Map<Id, sObject> is the core bulkification pattern after a single query

Nested collections (Map of Lists, List of Maps) are valid and common for grouping related records

  • Prefer one query plus Maps over SOQL inside loops; Sets prevent duplicate DML targets
  • Last updated: August 2026

6.2 Lists, Sets & Maps

Quick Answer: Apex collections are List (ordered, duplicates allowed), Set (unique elements), and Map (key→value). Platform Developer I heavily tests Map<Id, sObject> for bulkification: query once, index by Id, then process Trigger.new without SOQL or DML inside per-record loops. Use Sets to collect unique parent IDs and block duplicate DML.

Collections sit at the center of bulk-safe Apex. If you only memorize syntax, you will miss scenario questions; if you internalize when each structure wins, trigger and governor items become straightforward.

List: Ordered Sequences

A List is an ordered collection that allows duplicate values. SOQL returns List<sObject> by default. List<Account> accounts = [SELECT Id, Name FROM Account LIMIT 100];

  • List<String> statuses = new List<String>{ 'New', 'Working', 'Closed' };
  • statuses.add('New'); // duplicates allowed
  • String first = statuses[0];

Common List operations: add , addAll , get / index [] , size , isEmpty , clear , contains , remove , sort (when elements are comparable).

  • When to use List
  • Preserving query order or insertion order
  • Passing records to DML ( insert accounts )
  • Iterating every element including duplicates
  • Building result sequences for return values and batch chunks

Exam tip: List index is zero-based. Accessing an invalid index throws a runtime exception—check size() or use safe iteration.

Set: Uniqueness and Membership

A Set stores unique elements. Adding a duplicate is ignored (no error, no second copy). Set<Id> accountIds = new Set<Id>();

  • for (Contact c : Trigger.new) {
  • if (c.AccountId != null) {
  • accountIds.add(c.AccountId);
  • }
  • }
  • // accountIds has each parent Id at most once
  • Boolean hasId = accountIds.contains(someId);
  • When to use Set
  • Collecting related record IDs from Trigger.new without duplicates
  • Fast membership tests ( contains ) before expensive work
  • Deduplicating strings or enums before processing
  • Building SOQL bind collections: WHERE Id IN :accountIds

Sets do not guarantee order. Do not rely on iteration order for business sequencing—use a List if order matters.

Duplicate prevention pattern: Before DML, put candidates in a Map<Id, sObject> or track Ids in a Set so the same record is not updated twice in one transaction (duplicate Id in one update list causes errors).

Map: Keys to Values (Bulkification Hero)

A Map associates keys with values. The most important exam pattern is Map<Id, sObject> after a single query: Set<Id> accountIds = new Set<Id>();

  • for (Contact c : Trigger.new) {
  • if (c.AccountId != null) accountIds.add(c.AccountId);
  • }
  • Map<Id, Account> accountsById = new Map<Id, Account>([
  • SELECT Id, Name, Industry
  • FROM Account
  • WHERE Id IN :accountIds
  • ]);
  • for (Contact c : Trigger.new) {
  • Account parent = accountsById.get(c.AccountId);
  • if (parent != null && parent.Industry == 'Agriculture') {
  • // use parent fields without extra SOQL
  • }
  • }

The constructor new Map<Id, sObject>(listOfSObjects) indexes each record by its Id automatically—an idiomatic shortcut tested often.

Why Maps bulkify code

  • Anti-pattern Bulk-safe pattern
  • SOQL inside for (Contact c : Trigger.new) One SOQL with WHERE Id IN :ids , then Map lookup
  • DML inside per-record loop Build a List/Map of changes, one DML after the loop
  • Re-querying the same parent repeatedly accountsById.get(c.AccountId)

map.get(key) returns null when the key is missing—always null-check before using the value, or use containsKey first.

Other useful Map APIs: put , putAll , keySet() , values() , size() , isEmpty() , remove . for (Id accId : accountsById.keySet()) {

  • Account a = accountsById.get(accId);
  • }
  • for (Account a : accountsById.values()) {
  • // iterate records directly
  • }
  • Nested Collections

You can nest collections for grouping: Map<Id, List<Contact>> contactsByAccount = new Map<Id, List<Contact>>();

  • for (Contact c : [
  • SELECT Id, AccountId, Email FROM Contact WHERE AccountId IN :accountIds
  • ]) {
  • if (!contactsByAccount.containsKey(c.AccountId)) {
  • contactsByAccount.put(c.AccountId, new List<Contact>());
  • }
  • contactsByAccount.get(c.AccountId).add(c);
  • }

Common shapes:

  • Map<Id, List<sObject>> — children grouped by parent Id
  • Map<String, Set<Id>> — keys like external keys or statuses to Id sets
  • List<Map<String, Object>> — less common; sometimes used for generic payloads

Nested collections increase heap usage—still better than exploding SOQL rows inside loops, but avoid building huge graphs when a single aggregate query would suffice.

Choosing List vs Set vs Map

  • Need Prefer
  • Query results / DML input order List
  • Unique IDs / membership tests Set
  • Lookup related record by Id in a loop Map
  • Group children by parent Map<Id, List<T>>
  • Both uniqueness and later DML list Map values → List, or Set of Ids then requery

Id uniqueness: If you only need unique Ids for a bind variable, Set is enough. If you need the full parent record fields in a loop, Map<Id, sObject> is required.

Iteration Patterns That Show Up on the Exam

Traditional index loop — for (Integer i = 0; i < list.size(); i++) when you need the index.

List iteration — for (Account a : accounts) most readable for sObjects.

SOQL-for loop — for (Account a : [SELECT ...]) streams rows in chunks and is governor-friendlier for large result sets than loading one giant List (covered more with SOQL chapters).

Map key/value iteration — keySet() / values() as shown above.

Avoid modifying a List’s structure while iterating it with a for-each loop in ways that confuse indices; build a separate “to delete” or “to update” collection instead.

Duplicate Prevention with Sets (and Maps)

Scenarios:

  • Multiple Contacts in Trigger.new share one AccountId → Set collapses to one query key
  • Building List<Account> toUpdate → use Map<Id, Account> toUpdate and put by Id so each Account appears once

Same record qualified by two rules → Map last-write or merge fields deliberately Map<Id, Account> toUpdate = new Map<Id, Account>();

// ... business rules ...

  • toUpdate.put(a.Id, a); // last put wins for that Id
  • update toUpdate.values();
  • Exam Mindset

When a question shows SOQL inside a loop over Trigger.new, the fix is almost always: collect Ids in a Set , query into a Map , loop again with get . When a question asks which collection removes duplicates, answer Set (or Map keys). When order of insertion matters for processing, answer List .