Apex Syntax, Data Types & Collections

0/9 in this phase0/41 across the roadmap

šŸ“– Concept

Apex is Salesforce's proprietary, strongly-typed, object-oriented programming language. It runs on the Salesforce servers (not client-side) and is specifically designed for the multi-tenant environment.

Key characteristics:

  • Java-like syntax — if you know Java, Apex feels familiar
  • Strongly typed — all variables must be declared with types
  • Governor-limit-aware — designed to run within strict resource constraints
  • Database-integrated — SOQL/SOSL and DML are first-class citizens
  • Cloud-executed — runs on Salesforce servers, not client machines

Primitive Data Types:

Integer    — 32-bit whole numbers
Long       — 64-bit whole numbers  
Double     — 64-bit floating point
Decimal    — Arbitrary precision (for currency)
String     — Text (no character limit in Apex, but fields have limits)
Boolean    — true/false
Date       — Date without time
Datetime   — Date with time
Time       — Time without date
Id         — 18-character Salesforce record ID
Blob       — Binary data

Collections (the workhorses of Apex):

  1. List — Ordered collection (allows duplicates)

    • Indexed by integer position (0-based)
    • Most common collection for processing records
    • List<Account> accounts = new List<Account>();
  2. Set — Unordered collection of UNIQUE elements

    • Automatically deduplicates
    • Fast lookup (O(1) contains check)
    • Set<Id> accountIds = new Set<Id>();
  3. Map — Key-value pairs (unique keys)

    • O(1) lookup by key
    • The most powerful collection for bulkification
    • Map<Id, Account> accountMap = new Map<Id, Account>();
    • Can be initialized directly from SOQL: new Map<Id, Account>([SELECT ...])

Why Maps are critical: In bulkified code, you constantly need to look up related data. Without Maps, you'd need nested loops (O(n²)). Maps give you O(1) lookups, keeping your code performant within governor limits.

SObject — the universal data type: All Salesforce records are SObjects. Account, Contact, CustomObject__c are all subtypes of SObject. You can use SObject generically for dynamic code.

šŸ’» Code Example

codeTap to expand ā›¶
1// Apex Fundamentals — Data Types & Collections
2
3public class ApexFundamentals {
4
5 // Primitive types
6 public static void primitiveExamples() {
7 Integer count = 42;
8 Long bigNumber = 2147483648L;
9 Double pi = 3.14159;
10 Decimal price = 99.99; // Use Decimal for money!
11 String name = 'Salesforce Developer';
12 Boolean isActive = true;
13 Date today = Date.today();
14 Datetime now = Datetime.now();
15 Id accountId = '001000000000001'; // 15 or 18 char
16
17 // String methods
18 String upper = name.toUpperCase();
19 Boolean hasForce = name.contains('force');
20 String[] parts = name.split(' ');
21 String formatted = String.format('Hello {0}, today is {1}',
22 new List<Object>{name, today});
23 }
24
25 // LIST — Ordered, allows duplicates
26 public static void listExamples() {
27 // Declaration & initialization
28 List<String> fruits = new List<String>{'Apple', 'Banana', 'Cherry'};
29
30 // Common operations
31 fruits.add('Date'); // Add to end
32 fruits.add(1, 'Avocado'); // Insert at index
33 String first = fruits.get(0); // Access by index
34 fruits.set(0, 'Apricot'); // Replace at index
35 Integer size = fruits.size(); // Count
36 Boolean has = fruits.contains('Banana'); // Check existence
37 fruits.sort(); // Sort in place
38 fruits.remove(0); // Remove by index
39
40 // List of SObjects (most common usage)
41 List<Account> accounts = [SELECT Id, Name FROM Account LIMIT 10];
42
43 // Iterate
44 for (Account acc : accounts) {
45 System.debug(acc.Name);
46 }
47
48 // List initialization from SOQL
49 List<Contact> contacts = [
50 SELECT Id, FirstName, LastName, Email
51 FROM Contact
52 WHERE AccountId IN :accountIds
53 ];
54 }
55
56 // SET — Unordered, unique values only
57 public static void setExamples() {
58 Set<String> uniqueCities = new Set<String>();
59 uniqueCities.add('San Francisco');
60 uniqueCities.add('New York');
61 uniqueCities.add('San Francisco'); // Ignored — already exists!
62 System.assertEquals(2, uniqueCities.size());
63
64 // Most common pattern: collecting IDs for bulk queries
65 Set<Id> accountIds = new Set<Id>();
66 for (Contact c : contacts) {
67 if (c.AccountId != null) {
68 accountIds.add(c.AccountId);
69 }
70 }
71
72 // Use Set in SOQL WHERE IN clause
73 List<Account> relatedAccounts = [
74 SELECT Id, Name FROM Account WHERE Id IN :accountIds
75 ];
76
77 // Set operations
78 Set<String> setA = new Set<String>{'A', 'B', 'C'};
79 Set<String> setB = new Set<String>{'B', 'C', 'D'};
80 setA.retainAll(setB); // Intersection: {'B', 'C'}
81 // setA.addAll(setB); // Union
82 // setA.removeAll(setB); // Difference
83 }
84
85 // MAP — Key-value pairs (THE critical collection)
86 public static void mapExamples() {
87 // Basic Map
88 Map<String, Integer> wordCount = new Map<String, Integer>();
89 wordCount.put('apex', 5);
90 wordCount.put('soql', 3);
91 Integer count = wordCount.get('apex'); // 5
92 Boolean hasKey = wordCount.containsKey('soql'); // true
93 Set<String> allKeys = wordCount.keySet();
94 List<Integer> allValues = wordCount.values();
95
96 // ⭐ THE MOST IMPORTANT PATTERN: Map from SOQL
97 // Initialize Map<Id, SObject> directly from query
98 Map<Id, Account> accountMap = new Map<Id, Account>(
99 [SELECT Id, Name, Industry FROM Account WHERE Industry = 'Technology']
100 );
101
102 // Now O(1) lookup by ID!
103 Account acc = accountMap.get(someAccountId);
104
105 // Map for parent lookup in triggers
106 Map<Id, Account> parentAccounts = new Map<Id, Account>(
107 [SELECT Id, Name FROM Account WHERE Id IN :accountIds]
108 );
109
110 for (Contact c : Trigger.new) {
111 Account parentAcc = parentAccounts.get(c.AccountId);
112 if (parentAcc != null) {
113 // Use parent data without additional queries
114 System.debug('Contact belongs to: ' + parentAcc.Name);
115 }
116 }
117
118 // Map with complex keys
119 Map<String, List<Contact>> contactsByCity = new Map<String, List<Contact>>();
120 for (Contact c : contacts) {
121 String city = c.MailingCity;
122 if (!contactsByCity.containsKey(city)) {
123 contactsByCity.put(city, new List<Contact>());
124 }
125 contactsByCity.get(city).add(c);
126 }
127 }
128
129 // SObject — generic record type
130 public static void sObjectExamples() {
131 // Generic SObject usage
132 SObject record = new Account(Name = 'Test');
133 record.put('Industry', 'Technology'); // Dynamic field access
134 String name = (String) record.get('Name');
135
136 // Get the SObject type
137 Schema.SObjectType objType = record.getSObjectType();
138 System.debug(objType); // Account
139
140 // Cast to specific type
141 if (record instanceof Account) {
142 Account acc = (Account) record;
143 System.debug(acc.Name);
144 }
145 }
146}

šŸ‹ļø Practice Exercise

Apex Fundamentals Practice:

  1. Write a method that takes a List and returns a Map<String, Integer> counting character frequency
  2. Given a List of Contacts, build a Map<Id, List> grouping contacts by AccountId
  3. Write a method that deduplicates a List while preserving order (use Set + List)
  4. Create a method that merges two Map<String, Integer> objects, summing values for duplicate keys
  5. Write a utility that converts a List to a Map<String, SObject> using any specified field as the key
  6. Implement a simple stack (LIFO) using a List
  7. Write a method that finds the intersection of two List collections
  8. Build a frequency counter that returns the top N most common values from a List
  9. Write a method that takes generic SObject records and populates a specified field
  10. Create a batch-safe collection splitter that breaks a List into chunks of N items

āš ļø Common Mistakes

  • Using Double or Float for currency calculations — always use Decimal to avoid floating-point precision errors

  • Not initializing collections before use — 'List accounts;' is null, not empty. Always use 'new List()'

  • Forgetting that Map.get() returns null for missing keys — always check containsKey() or handle null returns

  • Using List.contains() for large lookups — O(n) per call. Use a Set for O(1) lookups when checking membership

  • Not understanding that Apex Strings are compared case-sensitively by default — use .equalsIgnoreCase() or .toLowerCase() for case-insensitive comparison

šŸ’¼ Interview Questions

šŸŽ¤ Mock Interview

Mock interview is powered by AI for Apex Syntax, Data Types & Collections. Login to unlock this feature.