Apex: Converting Lists To Sets And Vice Versa

Many Salesforce Apex developers are aware that in Salesforce, it’s widely accepted as a best practice that all code should be capable of handling bulk inserts, updates, and similar operations. When following this best practice, it’s common to use sets or lists as parameters in functions. Occasionally, there arises a necessity to convert between lists, sets, or even maps.

What distinguishes Sets from Lists?

Apex employs a hash structure for all sets. The key distinctions between sets and lists are that a Set is both unordered and unique. To access elements in a set, you must iterate through all of its items.

Therefore, you cannot write code like this:

Set<String> sStrings = new Set<String>{'a','b','c','d','e'};
 
String bString = sStrings[1];

Instead, your code should perform something along these lines:

Set<String> sStrings = new Set<String>{'a','b','c','d','e'};
 
string bString = null;
 
for (String s : sStrings)
{
    if (s == 'b') 
    {
        bString = 'b';
    }
}
 
system.assertEquals('b',bString);

Either method would be completely acceptable for a list.

What’s the rationale behind converting from a Set to a List, or vice versa?

I opt for Sets whenever I need to guarantee uniqueness among items or when I require to verify the presence of an item in the collection.

You can convert a List to a Set using the constructor of the Set class.
List<String> lStrings = new List<String>{'a','b','c','d','e'};
Set<String> sStrings = new Set<String>(lStrings);
You can convert from a Set to a List using the List constructor.
Set<String> sStrings = new Set<String>{'a','b','c','d','e'};
 
List<String> lStrings = new List<String>(sStrings);

Both types of collections offer an addAll method, which can add items to an existing collection.

There are also some interesting techniques for adding the IDs of an element to a Set, especially if you want to check whether an item has already been processed or similar scenarios.

For instance, if I’ve queried some accounts and need their IDs for processing, one way would be to iterate through all the accounts and add their IDs to a set. However, there’s a simpler method.

Utilizing the for-each approach:

List<Account> accounts = [select Id, Name from Account];
Set<Id> ids = new Set<Id>();
 
for (Account acc : accounts) 
{
    ids.add(acc.Id);
}
 
doSomethingWithIds(ids);

That method is acceptable, but it can become cumbersome over time. Instead, why not leverage the map and its functionalities?

Here’s an example where a List of Accounts is converted into a Set of IDs. I avoid querying an object and then immediately retrieving its list of IDs on the following line using this approach.

Instead, I would probably perform some form of filtering on the list within another method or elsewhere and then construct a set of the IDs.

List<Account> accounts = [select Id, Name from Account];
Set<Id> ids = (new Map<Id,Account>(accounts)).keySet().clone();
 
doSomethingWithIds(ids);
List<Account> accounts = [select Id, Name from Account];
Set<Id> ids = (new Map<Id,Account>(accounts)).keySet().clone();
 
doSomethingWithIds(ids);

The code snippet above converts the queried List of Accounts into a Map. Using keySet() retrieves the IDs from the map, and then .clone() allows us to modify the set of IDs. This step is necessary to avoid an exception being thrown due to the set not being modifiable otherwise.