This topic was originally presented by Philippe Ozil during an Apex Hours session on September 4, 2021.

Working with collections like List, Map, and Set is part of an everyday routine for Apex developers. While their basic use is straightforward, there are some advanced quirks that can get you to the next level. In this post, we’ll start with the basics about objects and collections, then we’ll dive into advanced concepts, such as iterators and sorting.

Exploring objects and collections

About objects and their methods

In Apex, all classes and primitives inherit from the Object class. This inheritance is implicit in the sense that you do not need to specify it when creating a custom class, for instance.

An inheritance from object

Thanks to the inheritance from Object, all object instances can use the equals, hashCode, and toString methods as demonstrated here:

equals let you compare an object instance for equality with another one. What’s interesting with this method is that it lets you compare object of different types. For instance, you can compare an Id with a String:

While equals is powerful, it has a performance cost when running on objects with large number of properties. This is where the hashCode method comes into play. hashCode returns an integer that identifies an object instance based on some of its property values. For example, the integer 123 has a hash code of 123 and a “Hello World” string has a hash code of -862545276.

Hash code values are pseudo-unique as some object instances with different property values may share the same hash code. This is called hash code collision and is considered acceptable. When a collision happens, equals is used as a fallback to compare objects. In any case, comparing two objects’ hash codes is statistically faster than calling equals.

The equals and hashCode methods are essential when working with collections, and we’ll see why as we explore the different collection types.

An overview of collection types

Object instances can be stored in three types of collections: List, Set, and Map. These collection types have different purposes and properties:

  • A List holds an ordered collection of non-unique elements
  • A Set holds an unordered collection of unique elements
  • A Map holds a dictionary of key values in which keys are unique but values are not

Fun fact: Set and Map are named after mathematical concepts (see Set and Map definitions).

List and Set have some degree of interoperability as they share some constructors and methods that work with both types. For example, you can build a List from a Set and vice-versa, and you can call methods like addAll with both types. This enables powerful uses cases, such as removing duplicate elements from a List by casting it into a Set:

The unicity of Set values and Map keys is enforced thanks to the hashCode method and a fallback to equals in case of hash code collision. These two methods are also essential for some Set and Map operations, such as Set.contains or Map.containsKey. Interestingly, List operations like List.contains solely rely on equals and never on hashCode.

Now that we’ve seen the big picture on objects and collection types, let’s dive into collection specifics. We’ll start by iterating on Lists and Sets.

Iterating on Lists and Sets

List and Set can be traversed with for loops, but there’s a powerful alternative: iterators. List and Set implement the Iterable interface, and this interface provides access to an iterator method that exposes an object that implements the Iterator interface.

Iterator’s hasNext and next methods

Thanks to the Iterator’s hasNext and next methods, you can traverse a collection in a single direction:

While the above code is more verbose than for loops, using iterators has three key advantages:

  • Iterators provide read-only access to collections. If you pass an iterator as a method parameter, you’re sure that this method cannot modify your collection.
  • Iterators lock the collection in read-only mode, preventing any modification to the collection while you are iterating on it:
  • Iterators provide a mechanism that let you dynamically retrieve elements on the go. Our core focus in this article is collections, but you can implement your own iterable classes. This is extremely useful when working with paginated data and when you retrieve elements in batches. For example, you could build a REST client that iterates on a resource, and you could start iterating without knowing the exact number of items that are available.

Check out iteration recipes in Apex Recipes for iterator examples along with their related test class.

Sorting Lists

The first choice when it comes to sorting records is generally to write a SOQL query with an ORDER BY clause. However, sometimes you need to sort records without fetching them from the database or even sort objects that are not records. In those cases, you can use the List.sort method like this:

The List.sort method is easy to use, but let’s take a closer look at how it works and how you can customize the ordering.

Working with the Comparable interface

List.sort works with List elements that implement the Comparable interface. The interface specifies a single method: compareTo that is called by the List sorting algorithm to order elements.

The interface specifies a single method: compareTo that is called by the List sorting algorithm to order elements

compareTo returns an integer with the following values:

  • 0 if this instance and objectToCompareTo are equal
  • > 0 if this instance is greater than objectToCompareTo
  • < 0 if this instance is less than objectToCompareTo

List.sort can sort any mix of objects from various types as long as they are primitives or they implements Comparable. For instance, you could perfectly write something like this:

As a word of caution, it’s safe to place objects that don’t implement Comparable in a List, but if you call the sort method on that List, you’ll get a System.ListException exception.

The sort method documentation provides an example implementation for sorting a custom class, so we won’t dive in details on this topic. But what about sorting SObject lists (list of records)?

Sorting lists of sObjects

SObject implements the Comparable interface and instances of that class have a predictable sort order. However, you may need to implement a custom sort order in some cases, and this is where things get more complex. The SObject class is final, so you cannot overwrite its internal methods, such as compareTo.

To compensate for that, you must work with a wrapper class around the SObject that you want to sort. Imagine that you are importing some accounts from a third-party integration and that you want to sort them based on the shipping country field before saving them. You can’t sort the records with SOQL since they are not yet in the database. You must implement the following class:

You could sort your List with your custom ordering logic in three steps:

  1. Convert the List<Account> into a List<SortableAccount>
  2. Call the sort method on List<SortableAccount>
  3. Convert the List<SortableAccount> back to List<Account>

However, implementing these steps is not practical as it would almost take as many lines of code as the implementation of SortableAccount and those extra lines wouldn’t be reusable. Fortunately, there’s something that you can do to reduce boilerplate code. Simply add the following static method to SortableAccount:

With this SortableAccount.sort static method, all it takes to sort a list of account records by shipping country is a single line:

Check out List recipes in Apex Recipes for the implementation of SortableAccount along with the related test class.

Sorting Lists with reusable comparators

While the default List.sort method is convenient, it has two important limitations:

  • The ordering logic is directly tied to the Comparable object that is being sorted
  • The sort method lacks the ability to sort with different strategies and parameters

The Java language (which is close to Apex) goes beyond the basic Apex sort method and exposes a convenient Arrays.sort(T[], Comparator) method where T is the type being sorted and Comparator an interface that specifies a compare method that works like Comparable.compareTo.

This pattern can easily be replicated in Apex with a custom ListUtils class and a Comparator interface:

custom ListUtils class and a Comparator interface

With this approach, you can sort Lists with different comparators and even pass parameters to comparators. You benefit from the fact that the ordering logic is decoupled from the objects that you sort.

Check out List recipes in Apex Recipes for the implementation of ListUtils, some comparators, and related test classes.

Closing words

That’s a wrap. We gave you a refresher on the Object class and the different collection types. We covered advanced collection concepts: you’ve learned how to use iterators and how to sort Lists with the default sort method and custom comparators. We hope this article helped to improve your understanding of collections. It’s now your turn to put this knowledge into practice in your projects.

Resources

About the author

Philippe Ozil is a Principal Developer Advocate at Salesforce where he focuses on the Salesforce Platform. He writes technical content and speaks frequently at conferences. He is a full stack developer and enjoys working on DevOps, robotics, and VR projects. Follow him on Twitter @PhilippeOzil or check his GitHub projects @pozil.

Get the latest Salesforce Developer blog posts and podcast episodes via Slack or RSS.

Add to Slack Subscribe to RSS