Lists. We all use them. We make lists before we go to the grocery store, we plan out our days or lay out our favorite movies.
In programming, I use lists a lot for many reasons. Sometimes I have multiple lists and want to compare them and see what’s in between:
- Does one list contains all the items in another list?
- Are the lists the same?
- Show me any elements from one list that exist in another list.
- Show me any elements from the first list that do not exist in the second.
- De-duplicating a list.
As we all know ColdFusion is built on top of Java. And because of that we can use the underlying Java methods. Ben Forta wrote a great article on using the underlying Java methods, so I’m not going to go into details here.
I just want to concentrate on lists and how we, as programmers, can use some methods that are not readily documented:
Lets take 2 simple lists.
<cfscript>
list1 = "1,2,3,4,5,6,7,8,9";
list2 = "2,4,6,8";
</cfscript>
We shall run these lists through a series of actions that one may find useful
For the Java methods that I shall be using here, we need to out the lists into an array. So lets just do that first and use the same array throughout
<cfscript>
arraylist1 = ListToArray(list1);
arraylist2 = ListToArray(list2);
</cfscript>
1. Does 1 list contains all the items in another list?
Using the Java method of containsAll this question is very easy.
<cfoutput>
Does list1 contain all in list2: #arraylist1.containsAll(arraylist2)#
</cfoutput>
2. Are the lists the same?
This time we shall use the Java method of equals.
<cfoutput>
Does list1 equal list2: #arraylist1.containsAll(arraylist2)#
</cfoutput>
3. Show me any elements from 1 list that exist in another list.
For me this is where things get a lot more useful. We shall use the Java method of retainAll.
<!--- Create a deep copy of the first list to keep it intact. --->
<cfset variables.finalList = duplicate(arraylist1)>
<!--- Retain any elements from the first list that do exist in the second. --->
<cfset variables.finalList.retainAll(arraylist2)>
<cfset variables.finalList = arrayToList(variables.finalList)>
<cfoutput>
Final List: #variables.finalList#<br>
Number of IDs the same: #listLen(variables.finalList)#
</cfoutput>
4. Show me any elements from the first list that do not exist in the second.
Again, this is something that I use a lot in my programming. I’ve set this up as a function that I simply pass in the 2 lists.
<!--- Create a deep copy of the first list to keep it intact. --->
<cfset variables.finalList = duplicate(variables.arraylist1)>
<!--- Remove any elements from the first list that exist in the second. --->
<cfset variables.finalList.removeAll(variables.arraylist2)>
<cfset variables.finalList = arrayToList(variables.finalList)>
<cfoutput>
Final List: #variables.finalList#<br>
Number of IDs not in second list: #listLen(variables.finalList)#
</cfoutput>
So very simple, yes?
But one could do that with cfloop no problems and with such small numbers of items in the lists, the overhead is negligible. But, what when we have a lot more items in the lists.
Here are some interesting numbers
Using the following lists
- List 1 contains 35417 numbers
- List 2 contains 728 numbers
I ran these 5 times using cftimer and the average times are below.
1. Does 1 list contains all the items in another list?
Takes 134ms
2. Are the lists the same?
Takes 18ms
3. Show me any elements from 1 list that exist in another list.
Takes 3692ms
4. Show me any elements from the first list that do not exist in the second.
Takes 1172ms
Now those numbers on their own don’t mean much, but lets compare them to cfloop numbers:
3. Takes 8115.2ms over twice as long.
4. Takes 37681.2ms. That’s a staggering over 32 times longer!
I think that you can see going the Java method route is well worth it.
Raw data
1.
117
100
217
119
117
2.
17
17
21
17
18
3.
3398
2869
3778
3787
4628
4.
883
999
1450
1175
1353
cfloop 3
7918
8122
7354
9318
7864
cfloop 4
38957
33179
37573
39038
39659
What are your thoughts?