July 30, 2011

XCollections - Using UDT with Map & Set in Apex !

Update Nov : 2011

XCollections is no more an independent project/API, its part of apex-commons now. XCollections is added to sub repository called  “collections” under apex-commons, all development and latest updates about it can be found here : https://github.com/abhinavguptas/collections. This post is also updated for hyperlinks and other details as per this recent change

What XCollections ?

Apex comes with a rich and continuously maturing library of native APIs. One of my favorite is Collections since my Java days. I always enjoyed presence of all 4 basic collections in Apex i.e.

  • Array & List : A list is an ordered collection of typed primitives, sObjects, user-defined objects, Apex objects or collections that are distinguished by their indices.
  • Set : A list is an ordered collection of typed primitives, sObjects, user-defined objects, Apex objects or collections that are distinguished by their indices.
  • Map : A map is a collection of key-value pairs where each unique key maps to a single value. Keys can be any primitive data type, while values can be a primitive, sObject, collection type or an Apex object.

We all know these collections do a good job as per their role and use cases.

What I’m missing Apex Collections !

Apex collections need to improve on a couple of areas for ex.

  1. Support for advanced sorting, not just sorting of primitives in List.

    We are lucky here, Richard Vanhook created an awesome library called apex-lang, now moved to git and known as apex-commons. That filled this advanced sorting gap and provided ability to do complex sorting using Java style Comparator/Comparable support. Please check ArrayUtils on Google Code project for more details.

  2. Support for Apex Objects OR UDT(User Defined Type/Classes) in Set & Maps

    Other piece of functionality that I really miss in Set & Maps is they don’t support Apex Objects or UDT(User Defined Types) as items or keys. Though most of the times in simple project we don’t need to use UDT’s with Set or Map, but on some occasions if this support is available. It could be a real time saver.

XCollections to provide missing UDT support in Sets & Maps !

imageXCollections is created to overcome the second limitation, i.e. to create an Apex API that will give UDT support in Sets & Maps.

So XCollections API is kick started with these two collections

1. XSet : One can add UDTs as item to this set.

2. XMap : One can use UDTs as keys to this Map.

Good news is that,  both these xcollections will support 99% methods available in Apex Set and Maps, with user defined apex classes Smile

Is your Apex Class or UDT – XKeyable ?

Guys form Java background must be aware that Set & Maps (Hash based) depend on two key methods in Object class i.e. hashcode() and equals(). It’s a good practice in Java to override and give a nice implementation of these methods when you are planning to use UDT's in Set/Map.

As Apex Object class is not having any such provision of hashcode() or equals() method, we required something to enforce end user to let XCollections know about similarity/equality of two different UDT instances. So, XCollections came up with a brand new interface called “XKeyable”, this interface is having a single method : “String getUniqueKey()” this method is the key for XCollections to know about two custom apex class instances being similar or different.

Here is code of XKeyable interface :

/**
        Client API looking forward to use XMap and XSet with UDT's should implement this interface for 
        UDT's participating as KEY in XMap and Item for XSet.
    */
    global interface XKeyable {
      /**
         Returns the Unique Key for this object, this Unique should be based on state.
         @return A unique String composite or single key for this state of object.
      */
      String getUniqueKey ();
    }

and here is a sample implementation :

global class Employee implements XKeyable {
       String name;
       String mobile;
       String landLine;
       Integer age;
       boolean isMale;
       String postalAddress;
       
      global Employee (String n, String m, String l, Integer a, Boolean im, String pa) {
        name = n; 
        mobile = m;
        landLine = l;
        age = a;
        isMale = im;
        postalAddress = pa;
      }
      
      global String getUniqueKey() {
        // assuming no two guys from same postal will carry same mobile and name :)
        return name + mobile + postalAddress;
      }
    }

So, in above code sample of Employee class, we easily implemented the getUniqueKey() method by just doing string concatenation of all the attributes that will comprise a unique composite key.

Now, this Employee class can be used in Xset as shown below:

static testMethod void testXSet() {
        XSet setx = new XSet();
        setx.add(new Employee('Abhinav', '232 256-1223', '432 456-3233', 17, true, 'Palam Vihar, Gurgaon, India'));
        // adding another employee with same key details i.e. name, mobile and postal. 
        setx.add(new Employee('Abhinav', '232 256-1223', '123 333-4444', 30, true, 'Palam Vihar, Gurgaon, India'));
        
        // Set should be of Size 1 as per uniqueness criteria
        System.assertEquals(1, setx.size());
        
        // adding another employee with different key details i.e. mobile no changed
        setx.add(new Employee('Abhinav', '555 333-2222', '123 333-4444', 30, true, 'Palam Vihar, Gurgaon, India'));
        
        // Set should be of Size 2 as per uniqueness criteria
        System.assertEquals(2, setx.size());
    }

Limitations of XCollections

One of the major limitation you will see with immediate effect is lack of Generics support. You can declare strongly typed collections in Apex for ex.

Set<string> aset = new Set<string>();
Map<id, Account> accountMap = new Map<id, Account>();

But as Apex doesn't supports Generics or Templates for user defined classes, XSet and XMap will not be that strongly typed. Similar code in XCollections format would be as follows.

XSet aset = new XSet();
XMap  accountMap = new XMap();

I know this is not cool, but it has to be like this until apex releases support for generics or templates. Sounds like strong candidate for IdeaExchange ?

~ XCollections road map ~

My plans with XCollections is to bring all Java collections API richness to it. Though here are some high level thoughts

  1. Support for User Defined Classes in Set & Maps via XSet and XMap, as discussed above.
  2. Add new collections like LinkedMap(ordered keys), TreeSet(Sorted Set) and other Java collections that are portable to Apex.
  3. Right rich suite of Apex test cases not just for sake for coverage, but for really testing all corners of the API Smile
  4. Give rich documentation and code samples !

Goal is to first give a stable version of XMap and XSet, and later move to rest of the items in roadmap.

XCollections on GITHUB !

All code so far is committed on github here :https://github.com/abhinavguptas/collections