Collections Eggstravaganza

Easter is right around the corner which got me thinking about easter eggs in software, movies, games, etc.  Easter eggs are purposefully hidden pieces of functionality that are usually interesting or fun in nature.  There are a few interesting pieces of functionality in Apex that, while not necessarily hidden from the public, are not obvious to everyone.  Particularly, the collection types in Apex (List, Map, and Set) have some interesting behaviors which I’ve documented below.

Lists in SOQL query

A Set of Ids can be used in an In expression in a SOQL query which is great to know.  More interesting, is that a List of sObjects can be used directly in an In expression without having to convert it to a Set.  The system automatically knows to use the Ids of the sObjects in the List.

Perhaps just as interesting is that you don’t have to use the In operator.  You can actually use the equals operator in that same SOQL query and it works.

Collection Conversions

A Map of Id to sObject can be created automatically by passing in a List of sObject to the overloaded Map constructor.  That is very useful to know on its own, however it is also possible to add additional lists to a Map that already contains items, using the Map.putAll method.

Likewise, the Set class has overloaded constructors that take a List argument and there is also an addAll method for Set.  A Set could be used to remove duplicates from a List.  There is something to watch out for, though, if implementing that.  The comparison done to check for equality compares all of the sObjects’ fields, not just the Id field.  So, a Set of sObjects may contain references to the same record (i.e, same Id), if there are differences in other fields.  That could happen if some fields are modified after being retrieved from the DB or could happen if the two sObjects were queried from the DB with different fields specified.  If you need to guarantee that kind of uniqueness and you don’t want to write any sort of wrapper class, you can just use a Map<Id, sObject> and use the containsKey method to check for existence prior to adding.

There are also overloaded List constructors and a List addAll that have Set parameters that simply convert to a List.  These are straightforward and don’t have any side effects like the automatic Map creation or Set filtering.

Collections with Multiple sObject Types

Collections can be declared to contain the super type sObject.  This can allow them to contain multiple types.  Closely related to this, is that when querying against a collection of Ids it is not necessary for the Ids to be Ids for the type that is being queried.  So, you could build up a List of sObjects and query back out of the DB just the certain type you need.

Collections of Objects

Closely related to collections of sObjects, is collections of Objects.  Apex supports declaring a collection to be of type Object.  That type can hold any object, including sObject types, so you could combine your own custom class objects with sObjects.

Collections of Objects and Collections

Going one step further, a List<Object> can actually contain other collections as well.  So, you could have something like the following:

Really, this is no different than just adding non-collection elements, since collections and non-collections are all instances of Object.  You can test that theory out by executing the following anonymously or by trying to save it in a class.

Something interesting happens.  A compile-time error of “Compile Error: Operation instanceof is always true since an instance of LIST<Account> is always an instance of Object” is generated.  While the system does communicate that the List<Account> is, in fact, an instance of Object, code with that check in will not compile.

Nested Lists in sObjects

Returning a defensive copy of a portion of an object in code prevents clients of an API from being able to modify the underlying / actual part of the object.  This is sometimes used with reference types like dates or collections to ensure that client code doesn’t modify something that is invariant about the object or to maintain control of modifications within the class itself.   Nested Lists in sObjects are defensive copies.  For example, each call to account.Contacts returns a different List reference.  So, if you add to the account.Contacts it does not persist on the in-memory List<Contact> attached to the Account.  Some code better illustrates this.

The above shows that the size hasn’t changed, but there is actually a way to know that the List<Contact> reference returned is different on each call.  Apex has a an exact equality operator, ===, that compares that the List<Contact> references the same location in memory.  The more common double equals, ==, does a deep comparison of all sObject field values.  The double and triple equals are officially referred to as the equality operator, and exact equality operator, respectively.  See the Apex documentation on expression operators for more.

Read-only Collections

Closely related to defensive copying is the concept of unmodifiable collections.  Unmodifiable collections do not support being modified, although they may still allow you to invoke methods on them that can modify them, with the result possibly being a run time exception.  In Apex, the Set returned by the Map.keySet() method returns an unmodifiable collection.  If you try to modify it you’ll get an error “System.FinalException: Collection is read-only”.

The other place where this read-only behavior can be observed is when iterating over a collection.  If code attempts to modify a collection while it is being iterated over a runtime exception, “System.FinalException: Cannot modify a collection while it is being iterated” will be generated.

If you need to modify the List or Set while iterating over it, use a simple for loop with a counter instead of the Set or List iteration.

Final Words

There are many different ways that the built-in collection types of List, Set, and Map can be used.  I’ve described some of the more interesting ones that I’ve run across, but I’m always interested in learning even more eggcellent uses, so share some eggsamples!

12 thoughts on “Collections Eggstravaganza

  1. Great list Peter! There were quite a few on there that I have never run into before. Apex has so many awesome little things like that. It is very cool to see.

    Also, nice use of the word “egg” all over the place! It reminded me of when I tried to do a Halloween themed article, but you pulled it off way better than I did!

  2. Thank you Peter for this look at Collections. It certainly was an informative read for me. It got me eggcited to dig into Collections.

  3. Great article, thanks!

    One way I like to use the map constructor from list is to quickly get the Ids from a list:
    list contacts = [select Id, Name from Contact limit 10];
    set contactIds = (new map(contacts)).keyset();

Leave a Reply

Your email address will not be published. Required fields are marked *