Apex Collections - El Toro - Find articles about Visualforce, Apex, Force.com and Salesforce in general

Print Preview

Apex Collections

A collection is a way to group multiple elements, which should be treated as a single entity. If you have used any other programming language, you should be familiar with this concept already.
 
In Apex, we also have collections, and although they are used more frequently, they are MUCH simpler than in other programming languages.
 
In Java, for example, there are different types of collections which include: HashSet, TreeSet, LinkedHashSet, ArrayList, LinkedList, HashMap, TreeMap, LinkedHashMap, ConcurrentSkipListSet, TreeSet and many, many more. Usually their difference has to do with how data is stored in RAM and how the computer resources can be most efficiently used, but in Apex (being a paas) those details are irrelevant.
 
In Apex, we have only three types of collections: Lists, Sets, and Maps.

List

A list is an ordered collection of elements (accessible by their indices) which can have duplicates.

List<String> myList = new List<String>();
myList.add('2');
myList.add(0, '1');
System.debug(myList.get(0)); // DEBUG|1
System.debug(myList); // DEBUG|(1, 2)
System.debug(myList.isEmpty()); // DEBUG|false
System.debug(myList.size()); // DEBUG|2
myList.remove(0);
System.debug(myList); // DEBUG|(2)
myList.add(0, '1');
System.debug(myList); // DEBUG|(1, 2)
myList.clear();
System.debug(myList); // DEBUG|()

Advanced examples of lists:

myList = new List<String>{'a','b','c'};
System.debug(myList); // DEBUG|(a, b, c)
List<Account> acs = [SELECT Id FROM Account LIMIT 2];
System.debug(acs); // DEBUG|(Account:{Id=001A0000016LoH8IAK}, Account:{Id=001A0000016LmxhIAC})

Set

A set is an unordered collection of elements that do not contain any duplicates. I like to think that adding elements to a set is like throwing them in a bucket, so therefore there is no order established and the elements can’t be indexed.

Set<String> mySet = new Set<String>();
mySet.add('1');
System.debug(mySet); // DEBUG|{1}
mySet.add('1');
System.debug(mySet); // DEBUG|{1}
mySet.add('2');
System.debug(mySet); // DEBUG|{2, 1}
System.debug(mySet.contains('1')); // DEBUG|true
System.debug(mySet.isEmpty()); // DEBUG|false
mySet.remove('2');
System.debug(mySet); // DEBUG|{1}
System.debug(mySet.size()); // DEBUG|1
mySet.clear();
System.debug(mySet); // DEBUG|{}

You can iterate over a set in the same way as a List, but the results are not necessarily displayed in the same order as they were added to the set.

mySet = new Set<String>{'a', 'b', 'c'};
System.debug(mySet); // DEBUG|{a, b, c}
for (String s : mySet) {
    System.debug('In Loop: ' + s);
}

When the Debug log is checked, this is what I see:

As mentioned before, the set values must be unique and that also applies for sObjects as shown in the next example

Account a1 = new account(name='MyAccount');
Account a2 = new account(name='MyAccount');
Account a3 = new account(name='MyAccount', description='My test account');
Set<Account> accountSet = new Set<Account>{a1, a2, a3};
System.debug(accountSet.size()); // DEBUG|2

Map

A map is a collection of key-value pairs where each unique key corresponds to a single value. The values should always be accessed by their keys.

Map<String, String> colorCodes = new Map<String, String>();
colorCodes.put('Red', 'FF0000');
colorCodes.put('Green', '00FF00');
colorCodes.put('Blue', '0000FF');
colorCodes.put('Black', 'FFFFFF');
colorCodes.put('Black', '000000');
System.debug(colorCodes); // DEBUG|{Black=000000, Blue=0000FF, Green=00FF00, Red=FF0000}
System.debug(colorCodes.size()); // DEBUG|4
System.debug(colorCodes.containsKey('Black')); // DEBUG|true
System.debug(colorCodes.containsKey('BLACK')); // DEBUG|false
System.debug(colorCodes.get('Black')); // DEBUG|000000
colorCodes.remove('Black');
System.debug(colorCodes.size()); // DEBUG|3
// Keys returned as Set<String>
System.debug(colorCodes.keySet()); // DEBUG|{Blue, Green, Red}
// Values returned as List<String>
System.debug(colorCodes.values()); // DEBUG|(0000FF, FF0000, 00FF00)
System.debug(colorCodes.isEmpty()); // DEBUG|false
colorCodes.clear();
System.debug(colorCodes); // DEBUG|{}

Some more advanced Map syntax include:

Map<String, String> mapStrings = new Map<String, String>{'a' => 'b', 'c' => 'd'};
System.debug(mapStrings); // DEBUG|{a=b, c=d}

Map<ID, Account> mapAccounts = new Map<ID, Account>([SELECT Id, Name FROM Account LIMIT 2]);
System.debug(mapAccounts); // DEBUG|{001A0000016LmxhIAC=Account:{Name=AAA, Id=001A0000016LmxhIAC}, 001A0000016LoH8IAK=Account:{Name=AAA, Id=001A0000016LoH8IAK}}

Map keys:

  • A map can have a null value key.
  • String keys are case-sensitive, and the methods put(), get(), containsKey(), and remove() treat these keys as distinct.
  • User-defined keys use the methods equals() and hashCode() provided in your classes
  • sObject keys compare the objects’ field values.

Data Types:

Keys and values in a map, or values in a List or a Set can be of any data type: primitive types, sObjects, user-defined types, or built-in Apex types. Additionally, you could have other collections making them multidimensional (up to 5 levels deep).

Set<String> setStrings = new Set<String>();
setStrings.add('a');
setStrings.add('b');
Map<Integer, Set<String>> mapSetStrings = new Map<Integer, Set<String>>();
mapSetStrings.put(1, setStrings);
mapSetStrings.put(2, new Set<String>{'c','d'});
System.debug(mapSetStrings); // DEBUG|{1={a, b}, 2={c, d}}

List<Account> accs = [SELECT Id, Name FROM Account LIMIT 2];
Map<Integer, List<Account>> mapListAccounts = new Map<Integer, List<Account>>{1 => accs};
System.debug(mapListAccounts); // DEBUG|{1=(Account:{Name=AAA, Id=001A0000016LoH8IAK}, Account:{Name=AAA, Id=001A0000016LmxhIAC})}

No Arrays in Apex?

Yes, but no! There are no arrays as such but a one-dimensional list can be accessed using the array notation. Like this:

List<Account> acs = [SELECT ID FROM Account LIMIT 20];
for (Integer i = 0; i < acs.size(); i++) {
	System.debug(acs[i]);
}

You can’t have multi-dimensional arrays, so you can’t do this:

Integer[][] integers1;
List<Integer>[] integers2;

But you can have this and it compiles successfully:

List<Integer[]>integers3;

But be careful because there are some very important differences, which can be highlighted with this code:

List<Integer> integersList1 = new List<Integer>();
Integer[] integersList2 = new List<Integer>();
Integer[] integersList3 = new Integer[2];
Integer[] integersList4 = new Integer[5];
List<Integer> integersList5 = new Integer[]{1};
Integer[] integersList6 = new Integer[]{1};
List<List<Integer>> listOfIntegersLists = new List<List<Integer>>();
listOfIntegersLists.add(integersList1);
listOfIntegersLists.add(integersList2);
listOfIntegersLists.add(integersList3);
listOfIntegersLists.add(integersList4);
listOfIntegersLists.add(integersList5);
listOfIntegersLists.add(integersList6);

for (List<Integer> integersList : listOfIntegersLists) {
    for (Integer j = 2; j < 5; j++) {
        integersList.add(j);
    }
    System.debug(LoggingLevel.ERROR, 'After add: ' + integersList);
    for (integer j = 0; j < 2; j++) {
        integersList.remove(0); 
    }
    System.debug(LoggingLevel.ERROR, 'After remove: ' + integersList);
    for (Integer j = 6; j < 8; j++) {
        integersList.add(j);
    }
    System.debug(LoggingLevel.ERROR, 'After add more: ' + integersList);
    for (Integer j = 0; j < 2; j++) {
        integersList[j] = 10 + j;
    }
    System.debug(LoggingLevel.ERROR, 'After setting first positions: ' + integersList);
    system.debug(LoggingLevel.ERROR, '==='); 
}

And this is the debug log, I got:

30.0 APEX_CODE,ERROR;APEX_PROFILING,ERROR;CALLOUT,ERROR;DB,ERROR;SYSTEM,ERROR;VALIDATION,ERROR;VISUALFORCE,ERROR;WORKFLOW,ERROR
19:22:41.057 (57598000)|EXECUTION_STARTED
19:22:41.057 (57604000)|CODE_UNIT_STARTED|[EXTERNAL]|execute_anonymous_apex
19:22:41.058 (58657000)|USER_DEBUG|[19]|ERROR|After add: (2, 3, 4)
19:22:41.058 (58742000)|USER_DEBUG|[23]|ERROR|After remove: (4)
19:22:41.058 (58807000)|USER_DEBUG|[27]|ERROR|After add more: (4, 6, 7)
19:22:41.058 (58911000)|USER_DEBUG|[31]|ERROR|After setting first positions: (10, 11, 7)
19:22:41.058 (58946000)|USER_DEBUG|[32]|ERROR|===
19:22:41.059 (59010000)|USER_DEBUG|[19]|ERROR|After add: (2, 3, 4)
19:22:41.059 (59060000)|USER_DEBUG|[23]|ERROR|After remove: (4)
19:22:41.059 (59093000)|USER_DEBUG|[27]|ERROR|After add more: (4, 6, 7)
19:22:41.059 (59202000)|USER_DEBUG|[31]|ERROR|After setting first positions: (10, 11, 7)
19:22:41.059 (59213000)|USER_DEBUG|[32]|ERROR|===
19:22:41.059 (59273000)|USER_DEBUG|[19]|ERROR|After add: (null, null, 2, 3, 4)
19:22:41.059 (59316000)|USER_DEBUG|[23]|ERROR|After remove: (2, 3, 4)
19:22:41.059 (59349000)|USER_DEBUG|[27]|ERROR|After add more: (2, 3, 4, 6, 7)
19:22:41.059 (59409000)|USER_DEBUG|[31]|ERROR|After setting first positions: (10, 11, 4, 6, 7)
19:22:41.059 (59419000)|USER_DEBUG|[32]|ERROR|===
19:22:41.059 (59477000)|USER_DEBUG|[19]|ERROR|After add: (null, null, null, null, null, 2, 3, 4)
19:22:41.059 (59520000)|USER_DEBUG|[23]|ERROR|After remove: (null, null, null, 2, 3, 4)
19:22:41.059 (59553000)|USER_DEBUG|[27]|ERROR|After add more: (null, null, null, 2, 3, 4, 6, 7)
19:22:41.059 (59585000)|USER_DEBUG|[31]|ERROR|After setting first positions: (10, 11, null, 2, 3, 4, 6, 7)
19:22:41.059 (59594000)|USER_DEBUG|[32]|ERROR|===
19:22:41.059 (59649000)|USER_DEBUG|[19]|ERROR|After add: (1, 2, 3, 4)
19:22:41.059 (59691000)|USER_DEBUG|[23]|ERROR|After remove: (3, 4)
19:22:41.059 (59724000)|USER_DEBUG|[27]|ERROR|After add more: (3, 4, 6, 7)
19:22:41.059 (59754000)|USER_DEBUG|[31]|ERROR|After setting first positions: (10, 11, 6, 7)
19:22:41.059 (59764000)|USER_DEBUG|[32]|ERROR|===
19:22:41.059 (59818000)|USER_DEBUG|[19]|ERROR|After add: (1, 2, 3, 4)
19:22:41.059 (59859000)|USER_DEBUG|[23]|ERROR|After remove: (3, 4)
19:22:41.059 (59891000)|USER_DEBUG|[27]|ERROR|After add more: (3, 4, 6, 7)
19:22:41.059 (59923000)|USER_DEBUG|[31]|ERROR|After setting first positions: (10, 11, 6, 7)
19:22:41.059 (59932000)|USER_DEBUG|[32]|ERROR|===
19:22:41.059 (59976000)|CODE_UNIT_FINISHED|execute_anonymous_apex
19:22:41.059 (59981000)|EXECUTION_FINISHED

comments powered by Disqus

© El Toro . IT @ 2013
Andrés Pérez