In Apex, a Map<K, V> is a collection of unique key → value pairs that lets you look up a value by its key in roughly constant time. Maps are the backbone of efficient, bulk-safe Salesforce code: they let you correlate records, cache query results, and keep SOQL and DML statements out of loops. This guide covers every core Map method, the powerful Map<Id, SObject> SOQL constructor, and the trigger patterns that keep you inside Salesforce governor limits.
Key takeaways
- A
Map<K, V>stores unique keys, each mapped to a single value; values may repeat, keys may not. get()returnsnullwhen a key is absent — it never throws an exception.- The
new Map<Id, Account>([SELECT ...])constructor auto-keys records by theirIdin a single line. Trigger.newMapandTrigger.oldMapare pre-builtMap<Id, SObject>maps — the canonical way to compare old vs new field values.- Building a Map keyed by a field is the standard bulkification pattern to pull SOQL and DML out of loops.
- Maps are unordered, and
Stringkeys are case-sensitive.
What is a Map in Apex?
A Map is one of the three Apex collection types, alongside List and Set. Each entry pairs a unique key with a value, and both the key and the value can be almost any data type — primitives, sObjects, user-defined classes, or even other collections.
You declare a Map with the Map<KeyType, ValueType> syntax and instantiate it with new. You can start empty, seed it from a map literal, or clone an existing map.
// Empty map: Id keys -> Account values
Map<Id, Account> accountsById = new Map<Id, Account>();
// String -> String, seeded with a map literal
Map<String, String> colorToItem = new Map<String, String>{
'red' => 'shirt',
'blue' => 'tshirt',
'black' => 'cap'
};
// Shallow copy of an existing map
Map<String, String> copy = colorToItem.clone();How do you add, retrieve, and check values?
Use put(key, value) to add or overwrite an entry, get(key) to read a value, and containsKey(key) to test for a key before reading. Because re-put-ting an existing key overwrites the old value, keys always stay unique.
Map<String, Integer> stock = new Map<String, Integer>();
stock.put('apples', 12);
stock.put('pears', 7);
stock.put('apples', 20); // overwrites 12 -> 20 (keys are unique)
Integer apples = stock.get('apples'); // 20
Integer mangoes = stock.get('mangoes'); // null (key absent, no error)
Boolean hasPears = stock.containsKey('pears'); // true
Integer count = stock.size(); // 2Apex Map methods reference
The table below lists the most-used Map<K, V> instance methods.
| Method | Signature | Returns | Description |
|---|---|---|---|
put |
put(key, value) |
V |
Adds the key/value pair; returns the previous value for that key (or null). |
get |
get(key) |
V |
Returns the value mapped to key, or null if the key isn't present. |
containsKey |
containsKey(key) |
Boolean |
true if the map contains an entry for key. |
keySet |
keySet() |
Set<K> |
Returns a Set of all keys in the map. |
values |
values() |
List<V> |
Returns a List of all values in the map. |
remove |
remove(key) |
V |
Removes the entry for key and returns its value (or null). |
size |
size() |
Integer |
Number of key/value pairs in the map. |
isEmpty |
isEmpty() |
Boolean |
true when the map has zero entries. |
clear |
clear() |
void |
Removes all entries from the map. |
putAll |
putAll(fromMap) |
void |
Copies all entries from another map (or an sObject list) into this map. |
clone |
clone() |
Map<K,V> |
Returns a shallow copy of the map. |
keySet() returns a Set<K> — so you can iterate the keys or feed them straight into a SOQL IN bind — while values() returns a List<V> you can loop over or pass to DML.
What does the Map<Id, SObject> SOQL constructor do?
One of the most useful Apex idioms is to pass a SOQL query straight into a Map<Id, SObject> constructor. Salesforce automatically uses each record's Id as the key, giving you instant Id-based lookups with no manual loop. Learn the query syntax in our guide on how to run a SOQL query in Salesforce.
// Auto-keyed by Account Id -- no loop needed
Map<Id, Account> accountsById = new Map<Id, Account>(
[SELECT Id, Name, Industry FROM Account WHERE Industry = 'Technology']
);
// Instant lookup by Id
Account a = accountsById.get(someAccountId);
// keySet() hands you all the Ids as a Set<Id>
Set<Id> accountIds = accountsById.keySet();How do you use Maps in Apex triggers?
Inside a trigger, Salesforce hands you two ready-made maps: Trigger.newMap and Trigger.oldMap, both typed as Map<Id, SObject>. They let you look up a record by Id and compare its old and new field values — the standard way to detect what changed. (Trigger.oldMap is null on insert, and Trigger.newMap is null on delete.)
trigger AccountTrigger on Account (before update) {
// Compare new vs old values to detect what changed
for (Account updated : Trigger.new) {
Account previous = Trigger.oldMap.get(updated.Id);
if (updated.Industry != previous.Industry) {
updated.Description = 'Industry changed from '
+ previous.Industry + ' to ' + updated.Industry;
}
}
}How do Maps help with bulkification and governor limits?
Salesforce enforces governor limits — hard caps such as 100 SOQL queries and 150 DML statements per transaction. The fastest way to blow past them is to run a query or DML inside a loop. Maps solve this: query once, load the results into a Map keyed by a correlating field, then look up related records in memory while you loop.
Below, instead of querying Contacts per Account, we collect the Account Ids first, run a single query, group the Contacts into a Map<Id, List<Contact>>, and correlate everything in one pass.
// 1. Collect parent Ids from the trigger set
Set<Id> accountIds = Trigger.newMap.keySet();
// 2. ONE SOQL query for all related children
Map<Id, List<Contact>> contactsByAccount = new Map<Id, List<Contact>>();
for (Contact c : [SELECT Id, AccountId FROM Contact WHERE AccountId IN :accountIds]) {
if (!contactsByAccount.containsKey(c.AccountId)) {
contactsByAccount.put(c.AccountId, new List<Contact>());
}
contactsByAccount.get(c.AccountId).add(c);
}
// 3. Correlate parent + children in memory -- no SOQL inside the loop
for (Account acc : Trigger.new) {
List<Contact> children = contactsByAccount.get(acc.Id);
Integer childCount = (children == null) ? 0 : children.size();
System.debug(acc.Name + ' has ' + childCount + ' contacts');
}Map vs List vs Set: which collection should you use?
Apex gives you three collection types. Pick the one that matches how you need to access the data.
| Feature | List |
Set |
Map |
|---|---|---|---|
| Structure | Ordered, indexed sequence | Unordered group | Key → value pairs |
| Duplicates | Allowed | Not allowed | Keys unique; values may repeat |
| Ordering | Index order preserved | Unordered | Unordered |
| Access by | Index (myList[0]) |
Membership test (contains) |
Key (myMap.get(key)) |
| Typical use | Ordered data, DML, query results | Unique values, IN filters |
Lookups, correlation, caching |
Reach for a List when order or indexing matters (see Apex List methods), a Set when you need uniqueness or fast membership checks (see Apex Set methods), and a Map when you need to look something up by a key.
Common Map gotchas to remember
- Maps are unordered. Never rely on
keySet()orvalues()returning entries in insertion order. Stringkeys are case-sensitive.'Red'and'red'are two distinct keys — even though Apex's==operator compares strings case-insensitively, Map keys do not. This trips up many developers.get()returnsnullfor a missing key, so null-check before dereferencing nested collections.keySet()returns the map's keySet— copy it if you need to modify the map while iterating.
Maps, Lists, and Sets together form the foundation of clean, bulk-safe Apex. Need a hand architecting triggers, batch jobs, or a full build? Our Salesforce development team has delivered 50+ projects since 2014.
Frequently Asked Questions
What is a Map in Salesforce Apex?
A Map is an Apex collection of unique key/value pairs. Each key maps to exactly one value, keys must be unique, and both keys and values can be any data type — primitives, sObjects, or custom classes. You use it to quickly look up a value by its key.
How do I get all keys or all values from an Apex Map?
Call keySet() to get a Set of every key, and values() to get a List of every value. keySet() is handy for binding into a SOQL IN clause, while values() gives you a list you can iterate or pass straight to DML.
What happens when you call get() on a key that does not exist?
get() returns null rather than throwing an exception. Use containsKey() first when you need to distinguish a missing key from a key that genuinely maps to a null value.
Are Apex Map keys case-sensitive?
Yes. For String keys, 'Account' and 'account' are treated as two separate keys, even though the Apex == operator compares strings case-insensitively. Normalize the case, for example with toLowerCase(), before using strings as keys if you want case-insensitive behavior.
What is the Map<Id, SObject> SOQL constructor used for?
Passing a SOQL query into new Map<Id, SObject>([SELECT ...]) builds a map automatically keyed by each record's Id. It is the quickest way to get Id-based lookups, and it is why Trigger.newMap and Trigger.oldMap exist as ready-made maps in triggers.
How do Maps help avoid Salesforce governor limits?
Maps let you query once and look up records in memory instead of running SOQL or DML inside a loop. By loading records into a Map keyed by a correlating field, you bulkify your code and stay well under the per-transaction SOQL and DML governor limits.