In my last blog post, I wrote about variables, and in particular, primitives. Today I’m going to expand on variables and talk about the oh-so-powerful idea of “collections.”
Unlike Java, which only has one type of collection (Arrays, also know as a Lists), Apex has three to choose from: Lists, Sets, and Maps. Together, we’ll go into each of these types of collections and show you what they are best used for, and how to use them when you start coding.
See how I just slipped that in there? Yes! You can (if you want to) code! Or, you can just explore these concepts, because…well…they’re fun to think about! And even if you don’t improve your understanding of Apex, you may improve your cooking skills!
Think of this as an amuse-bouche* for Apex collections. Hold on to your Chef’s hat, we’re diving in!
*From Wikipedia: An amuse-bouche [aˌmyzˈbuʃ] (plural amuse-bouches) or amuse-gueule [aˌmyzˈɡœl] is a single, bite-sized hors d’œuvre. Amuse-bouches are different from appetizers in that they are not ordered from a menu by patrons, but, when served, are done so free and according to the chef’s selection alone. These, often accompanied by a complementing wine, are served both to prepare the guest for the meal and to offer a glimpse into the chef’s approach to the art of cuisine.
What do I mean by a collection?
Collections, in general, are a group of things that can be handled as their individual elements, and can be treated as a whole.
For example, you can look at a bowl of fruit, and see that it is a collection of things. It contains a number of individual elements (apples, oranges, pears, grapes, bananas, etc.), but it can also be treated as a single thing (a bowl of fruit).
If you wanted to move the fruit from, say, the dining room table to the kitchen counter, you wouldn’t have to empty the bowl and move the fruit individually, you could just take the whole bowl, fruit and all, and move it (ooooh, feel the power!).
We can think of the bowl as a collection that includes items (elements) of fruit, in the same way that my previous post talked about a muffin tin being a container that holds many muffins. (But given my hunger for the subject, not as many as last week)
Why would we need collections?
Before I go into the types of collections, let’s talk, in generalities, about why you would need them? Just as I intimated in the bowl of fruit example above, collections are handy because they can be acted on as a whole, gathering together a number of identical actions for each of the “bowl’s” contents.
Say we were out in our imaginary orchard (oh, how I wish I had one!) and we were looking at all the ripe fruit on our trees. If we wanted to pick a selection of fruit to bring inside, we wouldn’t pick an apple and walk it inside, then go back out and pick an orange and bring it inside, and then go back out, pick a lemon and come back inside. Right?
Wouldn’t it be much simpler to bring out a basket, pick the fruit, put them in the basket, and then bring the basket with all its fruit inside? Much better!
In the same way, we can use collections to store things, until such time as we want to use them. Just as we can add fruit to our bowl, we can also remove (mmm…and eat) them. We can do many other things with our collection, like count how many we have in the bowl before and after we munch on them.
All of these things (and more) we can also do with collections.
Types of collections
As mentioned at the beginning of this post, Apex has three different types of collection: Lists, Sets, and Maps.** Now we’ll get into the truly exciting stuff: what makes them special and how to use them.
** There is another type of list called an Enum (great name right?!), but it’s a little different from the rest, so I’m going to set that aside for now.
According to the Force.com Apex Code Developer’s Guide, a list is an “ordered collection of elements that are distinguished by their indices.”
That’s a mouthful (pun intended) so let’s break it down. A list is:
- A collection of elements = a group of things
- An ordered collection = each member of the list has a specific place within the list. Kind of like an address.
- The order of the elements is distinguished by its index = we can tell where each element lives, because it has a home address. We can point to (and use) a particular item in the list using its address.
Let’s take our bowl of fruit, and stretch it out…literally, so it becomes an orderly platter of fruit; oh, and for the sake of a good visual, let’s give the platter some little ridges that keep our fruit from sliding into each other.
If we were to superimpose our platter of fruit with the list variable, we see that we have one variable that represents the list as a whole, and that we can reference each element in the list by its index (or its numeric address).
You can see that the home address (index) for the platterOfFruit starts at zero. This is just one of those rules you have to remember. Unlike most things in life, in Apex (like most coding languages), we start counting our elements in our list from zero. The 1st element of the list index = 0, the 2nd element’s index = 1, the 3rd element’s index = 2, etc.
In Apex code, we can create a list variable with the following syntax:
List<String> platterOfFruit = new List<String>();
What we’re doing here is creating a new variable called platterOfFruit, and saying its data type is a List of Strings. Another way to say that is we’re declaring the platterOfFruit variable to be a List of Strings.
When you create any collection, you always have to state what kind of thing (data type) the list contains. Not to be confused with the data type of the collection itself, which is, in this case, a List.
We can reference any of the elements in our List by using the address (index) for the element. For example, in our platterOfFruit, we could put a piece of fruit, say an orange, into the second platter slot, by using the following code:
platterOfFruit = 'Orange';
Again, the first slot would have an index of 0, and the second slot (that holds the orange) has an index of 1. We’ll go ahead and also put an orange in the fourth slot (with the index of 3).
platterOfFruit = 'Orange';
In this example, we put a piece of fruit in the second slot, but nothing in the first. Does the first slot exist? Good question. Yes, the first slot (element) does exist, but it is NULL. Null is the absence of a value (not an empty string, not a zero, but literally “nothing”). It’s a placeholder for when something is placed in the slot.
But that’s enough on lists for now…what about sets?
We’ll again call on the definition provided by the Salesforce’s Apex Developer’s Guide, which says that a set is an “unordered collection of elements that do not contain any duplicates.”
Again, breaking this down, a set is:
- A collection of elements = a group of things (same as a list)
- An unordered collection = we can’t say where each of the elements live, they are just in there somewhere.
- A collection of elements that doesn’t contain duplicates = each value can only be added to the set once.
So, while a list is very orderly and could possibly contain two or more identical items, a set doesn’t have any order and each element (thing) can only exist once in the collection (group of things).
Note, Sets can be fed duplicate elements (things), but they’ll just ignore them, “Sorry, I already have an orange, I’ll just pretend you didn’t just try to give me another (…how embarrassing!)”
You could create a Set using the following syntax:***
Set muffinIngredients = new Set(); muffinIngredients.add('flour'); muffinIngredients.add('baking powder'); muffinIngredients.add('salt'); muffinIngredients.add('nutmeg'); muffinIngredients.add('eggs'); muffinIngredients.add('milk'); muffinIngredients.add('sugar'); muffinIngredients.add('butter'); muffinIngredients.add('vanilla');
***Likely this would actually be two Sets, one for wet ingredients and one for dry, because in reality we would combine all the dry ingredients together in one bowl, and separately all the wet ingredients together in another, and then combine the wet with the dry…Did I lose you? Never mind…we’ll hit multi-dimensional collections in another post.
List vs Set?
So, when should I use a list and when should I use a set? That’s a great question.
Lists are used a lot because they are very flexible. Lists can contain duplicate items (e.g., you can have more than one orange in your platter), and they can be used when you want to order things (e.g., perhaps we’re extremely tidy and want to display our fruit in a particular order). But sets have their value too.
When deciding between using a list or a set, just ask yourself two questions:
- Is order important?
- Do I want to allow duplicates?
If the answer is “yes” to either of these questions, then use a list. Otherwise, use a set.
We’ve covered lists and sets, but what’s with this “Map” thing?
Referring again to our trusty Apex reference, maps are a “collection of key-value pairs where each unique key maps to a single value.” Say what?! Let’s break that waaay down:
- A map is a collection that contains key-value pairs = a group of things where each thing is broken down into two parts: a key and a value.
- Within a map, each unique key maps to a single value = there are no duplicate keys, and each key can have only one value.
Not included in the definition, but nonetheless true, Maps are unordered collections, so in order to access the value for an individual element, you would reference its key. The key acts like an ID field for the value.
But maybe it’s still not clear as to what is a key, and what is a walue? I admit that maps are much easier to understand with an example. Let’s create a map of flavors:
Map<string, string=""> myFlavors = new Map<string, string="">(); myFlavor('Honey', 'Sweet'); myFlavor('Anchovies', 'Salty'); myFlavor('Vinegar', 'Tart');
You can say to yourself, “as Honey is Sweet, so Anchovies are Salty, and Vinegar is Tart.”
In this example, ‘Honey,’ ‘Anchovies,’ and ‘Vinegar’ are all keys, and ‘Sweet,’ ‘Salty,’ and ‘Tart’ are all values. Think about what else you could find in your kitchen that might have a particular flavor? What about Peppermint, or Habanero Chili, or Onion? What could their values be?
So now we know what keys and values are and how to add key-values to a map, but how do we get one of a maps values back out?
Just like in a database, when you want to pull out a record, you’d use the ID (key). So if I wanted to pull out the flavor (value) of my favorite Caesar Salad ingredient, then I would use ‘Anchovies’ as the key:
String value = myFlavor.get('Anchovies');
And I would be rewarded with the value of ‘Salty.’
How cool is that?!
Ready for a curveball? What if I had in my Map variable pairs with the keys of ‘Orange’ and ‘Apple’? Wouldn’t they both have the flavor of ‘Sweet’? Is that allowed?
Yep. A map can have duplicate values, but just like in a database, the ID (key) must be unique. So while you can only have one ‘Orange,’ it and other keys can have a value of ‘Sweet.’
What can I do with collections?
We talked a little at the beginning of this post about why you would want to use collections. But let’s take things just a little bit further, because you can do so much with them.
They are great ways to temporarily store groups of things (collections of elements) without a lot of fuss (or processing space). Once you put things in a collection, you can set them aside till you want to use them, without going to all the trouble of inserting them into your database.
Let’s take things out of the kitchen and into Salesforce for a sec. Say you want to do something to a group of Contacts; perhaps add the same activity to a group of them.
You could go into each individual contact record and hand enter an activity (to call the person on Tuesday), but that seems like a lot of work. And the more contacts you want to add the activity to, the more work you have to do.
This is the equivalent of our orchard example above…imagine if you were responsible for picking all the ripe fruit and bringing it inside? Phew! Doing that one at a time would be exhausting! There has to be a better way…and thankfully, there is. This is where collections come in.
Just as we would pick fruit and put it in a basket, which we would then bring inside, we can create a collection, feed it our contact IDs, and then run through them all together as a group, and create the new activities. Much better!
If I were to write this out in plain English (also known as pseudo code), it might look something like this:
// Step One: Create a set called myContacts // Step Two: Add to myContacts the IDs for each of the contacts I want to work with // Step Three: Take each contact in the list and add an activity (call this person on Tuesday).
Another way to say the last step would be “loop through the set” and create an activity. We’ll be going into loops in another post, but you can think of them as a way to repeat some set of actions for each item (element) within a collection.
Why did I use a set above instead of a list? Because I didn’t want duplicates and the order of the contacts in the collection was not important to me.
Using Sets and Maps in Apex Triggers
So one of the things that we haven’t talked about yet, is that all three collections are not only able to store simple variables (like numbers, strings, and boolean values), but we can also use them to store complex variables…like whole entire Salesforce records. No Way!
In Apex terminology records are called objects – yes, this is a little strange if you’re a Salesforce Admin, because we’re used to thinking of “objects” as a place that holds records (e.g., the Contact object, the Account object, etc.). But we’ve crossed over to the dev side, so now we call records, “objects,” and we have a new name for Salesforce objects: sObjects.
For more about how to think about objects, see my blog post: How buying a major appliance can help you understand Object-Oriented Programming.
I know I may have just lost some of you, and that’s ok. This is tricky stuff, but bear with me for a bit longer. Back to the amazingly cool thing about collections…you use them to store entire Salesforce records. Now, tell me that isn’t neat!
And this is why Sets and Maps are so useful in Apex Triggers.
Sets are great because they are auto-magically du-dupified. (Yes, this is my Blog and I can make up words if I want to!)
So we can send sets all the IDs for records we just did something with (e.g., created, edited or deleted) and they’ll create an unduplicated group for these IDs. Then we can then use these IDs to query Salesforce and store the results of the query in a map.
This means we now have a basket of Salesforce records that we can use however we want. Now that’s power! (And the topic of a future post on Apex Triggers.)
Phew! Congratulations…you made it!
By now, I hope you agree with me that collections can be useful (and delicious!). They’re super helpful as temporary storage and there are a lot more things that we can do with them that I haven’t covered here (think counting, ordering, comparing, etc.).
To boil it all down (so to speak):
- Use lists when you want either an ordered group or the possibility of storing duplicates.
- Use sets when you want an unordered group of items that do not contain duplicates.
- Use maps when you want to collect items in key-value pairs (e.g., Salesforce ID-Salesforce record)