Apex language & collections
By tonight: you write fluent Apex with Lists, Sets and Maps, and you understand why every Salesforce interview circles back to the Map pattern.
-
Open theory — the language & the runtime
- What Apex is: a Java-like, strongly-typed language that runs on Salesforce servers in a multitenant runtime. Because thousands of orgs share hardware, every transaction is metered by governor limits — that single fact explains most Apex "best practices."
- Case-insensitive (
Account=ACCOUNT), compiled and stored on-platform, versioned per class (API version on each class). - Primitives:
Integer, Long, Decimal, Double, String, Boolean, Id, Date, Datetime, Time, Blob. Currency fields arrive asDecimal. Everything can benull— guard with safe navigationacc?.Owner?.Nameand null-coalescingx ?? 'default'. - Classes:
public | private | global,with/without sharing(Day 7),staticvs instance. Static variables live for one transaction — that's why they work as recursion guards (Day 5). - sObjects: every record is an
SObject(Account a = new Account(Name='Acme');). GenericSObject+a.get('Name')/a.put(...)enable dynamic code.
Run it — two ways
- VS Code:
Cmd+Shift+P → SFDX: Execute Anonymous Apex(select code first), output in the Debug console. - Dev org: Setup → Debug → Developer Console → Debug → Open Execute Anonymous.
public class GreetingService { private static final String PREFIX = 'Hello, '; public static String greet(String name) { return PREFIX + (name ?? 'stranger'); } } // Anonymous: System.debug(GreetingService.greet('Amardeep')); -
Open theory — collections & the three golden idioms
- List — ordered, allows duplicates, index access.
add, get, size, sort, isEmpty, addAll, clear. - Set — unique values, no order, O(1)
contains. Use for dedupe and "collect Ids" steps. - Map<K,V> — key → value.
put, get, containsKey, keySet(), values(). Keys are compared by value (Ids, Strings are common keys).
Idiom 1 — query straight into a Map
Map<Id, Account> accById = new Map<Id, Account>( [SELECT Id, Name, Industry FROM Account WHERE Industry = 'Banking'] ); // accById.keySet() is now a Set<Id> you can bind in another queryIdiom 2 — group children by parent (memorize)
Map<Id, List<Contact>> contactsByAcc = new Map<Id, List<Contact>>(); for (Contact c : [SELECT Id, AccountId FROM Contact WHERE AccountId != null]) { if (!contactsByAcc.containsKey(c.AccountId)) { contactsByAcc.put(c.AccountId, new List<Contact>()); } contactsByAcc.get(c.AccountId).add(c); }Idiom 3 — collect, then act once (the bulk shape)
Set<Id> accIds = new Set<Id>(); for (Opportunity o : opps) { accIds.add(o.AccountId); } // collect Map<Id, Account> accs = new Map<Id, Account>([ SELECT Id, OwnerId FROM Account WHERE Id IN :accIds // one query ]); for (Opportunity o : opps) { // act in memory o.OwnerId = accs.get(o.AccountId)?.OwnerId; }- SOQL-for loop
for (Account a : [SELECT ...])processes in 200-record chunks — heap-friendly iteration for big result sets. - List — ordered, allows duplicates, index access.
-
Open the six katas + acceptance criteria
- Dedupe: given
List<String> emailswith duplicates, produce a deduped list preserving first-seen order. (Set + List.) - Group: query Contacts, build
Map<Id, List<Contact>>by AccountId (Idiom 2 from memory). - Totals: from a
List<Opportunity>(create 10 in-memory), buildMap<Id, Decimal>AccountId → summed Amount. - Domain split: from Contact emails, build
Map<String, Integer>domain → count (hint:email.split('@')[1], guard nulls). - Diff: two
Set<Id>A and B — compute A∩B and A−B usingretainAll/removeAllon clones. - Reshape: turn
Map<Id, List<Contact>>intoMap<Id, Integer>(counts), then find the accountId with max count.
Done when: each runs clean in Execute Anonymous with
System.debugproof, no hardcoded Ids, and pushed to GitHub. - Dedupe: given
-
Questions + key points to hit
- List vs Set vs Map — when each? (order/dupes vs uniqueness/contains vs lookup-by-key; give the trigger use case for each)
- Why does Apex have governor limits? (multitenancy — shared resources; limits force bulk-safe design)
- static vs instance? (per-transaction lifetime of statics; utility methods; recursion guards)
- What is an sObject? (generic record type; concrete vs generic; dynamic get/put)
- How do you run ad-hoc Apex? (Execute Anonymous — VS Code / Dev Console)
Structure every answer: definition → when to use → gotcha → example from today's katas.