I’m still using CF7 for most things. CF8 has a lot of performance improvements, especially when it comes to list processing. But CF7’s list processing is notoriously bad. For small lists, it’s fine, but as the list grows, the speed drops very severely. It’s actually so bad, it’s almost funny.
Here’s what I’ve discovered.
CF7’s list functions operate using string tokenization, or at least so I’ve heard. For example, if you were to do a cfloop over a list containing 4 items, it would read the string (the whole list) up until it found the first delimiter, and set that value to the index. The second trip through the loop, it would grab up to the first delimiter, discard it, grab to the next delimiter, and set that value to the index. On the third trip through, it would grab the first delimiter, discard it, grab the second delimiter, discard it, get the 3rd value, and set it to the index. You can see how this would get very bad when the list starts getting longer. ListLen() does about the same thing to get the length of the list. listGetAt(list,100) will get the first 99 entries, discard them, and then get the 100th.
The solution that’s often presented is to use the listToArray() function to convert the list to a CF array. It’s then pretty quick to get the length, and access elements by their index without doing the tokenization step. The issue is that listToArray() itself is not that fast. Especially not when compared to the creation of a String[] (array) in java using the split() method on a String. I only recently have figured out why. I had a list of integers, converted to an array. I grabbed one of those array elements, and attempted to perform a java native string method on it, which blew up. I dumped a .getClass().getName(), and lo and behold, it was an Integer. How did it know it was an integer? The listToArray() method! It must inspect every item in the list. Converting a several thousand item list to an array with listToArray(), takes several hundred milliseconds.
So, using the java native split() method is faster, and leaves everything as strings. Which is just fine by me. CF doesn’t know the difference anyway. I tried this out, and it seemed to work fine, at first. But I quickly ran into two issues.
First, if the list only contains a single item, and it’s a numeric, CF treats it as a numeric, so the split() method doesn’t exist. Simple enough to fix, javacast(’string’,list).split(javacast(’string’,',’)) — take care of both the list and the delimiter.
Second, is that the result of the split is a String[]. CF sees this as an array, but the CF array functions such as arrayAppend() won’t work with it. CF arrays, coldfusion.runtime.Array in java speak, extends the java.util.Vector class. Vector won’t take a String[], but it will take an ArrayList. The java.util.Arrays class has an asList() method that takes a String[] and converts it to an ArrayList, just what we need so:
createObject(’java’,'java.util.Vector’).init(
createObject(’java’,'java.util.Arrays’).asList(
javacast(’string’,list).split(javacast(’string’,',’))
)
);
BooYah! - now we can take a 10,000 item list, convert it to an array, loop over it, and search it (with the .indexOf() method) very efficiently. Due to the inspection of the items when building the array, listToArray() is still slower than this method on CF8.