The Apex Master Class

What Are Maps in Apex? (Apex Master Class Ep. 19)

4 min read

Subscribe on YouTube

What Is A Map?

A map is a collection of key/value pairs, where each unique key points to a single value.

I’ll be honest with you: when I was learning to code, maps made me nervous. That definition meant nothing to me and it took me far too long to work out what was actually going on. So if your eyes just glazed over, you’re in good company and we’ll fix it right now.

Think of a dictionary. You look up a word and you get its definition. The word is the key. The definition is the value. You don’t read the dictionary front to back looking for your word, you jump straight to it.

That’s a map.

Apex
Map<String, String> tacoPrices = new Map<String, String>();

tacoPrices.put('Crunchy', '$1.29');
tacoPrices.put('Soft', '$1.49');

System.debug(tacoPrices.get('Crunchy'));   //$1.29

Two types in the angle brackets this time: the key type first, then the value type. put() to add a pair, get() to look one up.


Keys Are Unique, Values Are Not

Each key can only appear once. Put the same key in twice and the second one silently overwrites the first:

Apex
tacoPrices.put('Crunchy', '$1.29');
tacoPrices.put('Crunchy', '$1.99');   //inflation

System.debug(tacoPrices.get('Crunchy'));   //$1.99
System.debug(tacoPrices.size());           //1

No error, no warning. It just replaces it. Worth remembering when you’re building a map inside a loop and wondering where half your data went.

Values have no such restriction. Ten keys can all point at the same value quite happily.


The Methods

Code
tacoPrices.put('Soft', '$1.49');
tacoPrices.get('Soft');           //$1.49
tacoPrices.containsKey('Soft');   //true
tacoPrices.remove('Soft');
tacoPrices.keySet();              //Set of all the keys
tacoPrices.values();              //List of all the values
tacoPrices.size();
tacoPrices.isEmpty();

keySet() and values() are the two you’ll lean on most. Note the types: keys come back as a Set (because they’re unique), values come back as a List (because they’re not). That’s a nice bit of design once you notice it.

Ask for a key that isn’t there and you get null rather than an exception, so use containsKey() when the difference matters.

Full details in the Map class documentation.


The Trick That Makes Maps Worth It

Here’s the one that’ll make maps click. You can hand a list of SObjects straight into a Map constructor and Apex builds an Id-to-record map for you, for free:

Apex
Map<Id, Account> accountsById = new Map<Id, Account>(
    [SELECT Id, Name FROM Account LIMIT 200]
);

//Now grab any account instantly by its Id
Account acct = accountsById.get(someAccountId);

One line. No loop. It uses the record Id as the key automatically.

And this is why maps matter so much in Salesforce. Say you’ve got 200 Contacts and you need each one’s Account name. Without a map, your instinct is to query inside the loop:

Apex
//ABSOLUTELY NOT. 200 contacts = 200 queries = dead transaction.
for (Contact cont : contacts)
{
    Account acct = [SELECT Name FROM Account WHERE Id = :cont.AccountId];
    System.debug(acct.Name);
}

That hits the 100 query governor limit and dies. Here’s the same thing done properly:

Apex
//Gather the Ids (Set, so no duplicates)
Set<Id> accountIds = new Set<Id>();
for (Contact cont : contacts)
{
    accountIds.add(cont.AccountId);
}

//ONE query, straight into a map
Map<Id, Account> accountsById = new Map<Id, Account>(
    [SELECT Id, Name FROM Account WHERE Id IN :accountIds]
);

//Now look each one up instantly. No more queries.
for (Contact cont : contacts)
{
    Account acct = accountsById.get(cont.AccountId);
    System.debug(acct?.Name);
}

Two hundred queries down to one. That pattern, gather Ids into a Set, query into a Map, look up inside the loop, is the backbone of basically every well written trigger and service class you will ever read.

Learn it now and you’ll be ahead of a startling number of working Salesforce developers.


Maps Of Lists

One more that looks scary and isn’t. Sometimes one key needs to point at several things, like all the Contacts on an Account:

Apex
Map<Id, List<Contact>> contactsByAccountId = new Map<Id, List<Contact>>();

for (Contact cont : contacts)
{
    if (!contactsByAccountId.containsKey(cont.AccountId))
    {
        contactsByAccountId.put(cont.AccountId, new List<Contact>());
    }
    contactsByAccountId.get(cont.AccountId).add(cont);
}

That containsKey check is doing something important: the first time you see an Account Id there’s no list there yet, so you have to create an empty one before adding to it. Skip it and you’ll be calling .add() on null, and we all know how that ends.


Which Collection Do I Use?

List – ordered things, duplicates fine. Query results, records to update.
Set – unique things, order irrelevant. Gathering Ids, “have I seen this?” checks.
Map – looking something up by a key. Avoiding queries inside loops.

When you’re not sure, ask what question you’ll be asking of it later. “Give me the third one” is a List. “Is this in here?” is a Set. “What goes with this?” is a Map.


What’s Next

That’s all three collection types done. In the next episode we move on to the static keyword, which is one of those things that seems simple until somebody asks you to explain it.

See you next time!


Get Coding With The Force Merch!!

We now have a redbubble store setup so you can buy cool Coding With The Force merchandise! Please check it out! Every purchase goes to supporting the blog and YouTube channel.

Get Shirts Here!
Get Cups, Artwork, Coffee Cups, Bags, Masks and more here!


Check Out More Coding With The Force Stuff!

If you liked this post make sure to follow us on all our social media outlets to stay as up to date as possible with everything!

Youtube
Patreon
Github
Facebook
Twitter
Instagram


Salesforce Development Books I Recommend

Advanced Apex Programming
Salesforce Lightning Platform Enterprise Architecture
Mastering Salesforce DevOps

Good Non-SF Specific Development Books:

Clean Code
Clean Architecture