String methods: ColdFusion vs. Java
You may know from previous blog posts that I strongly advise every ColdFusion developer to familiarize himself/herself with the thing that actually makes ColdFusion tick, i.e. with Java. Everybody who writes a single line of CFML should know about the possibilities of extending ColdFusion by directly accessing the underlying Java methods of certain objects. One of the datatypes where actually using Java may make a lot of sense is the string object.
ColdFusion string literals are just plain old Java strings. If you grab a string from e.g. a query object like variables.qMyQuery.myTextColumn, you need to be careful though - even if you think you just have one tuple returned, you've got something other than a string object on your hands. In such a case you need to either specifically target a certain row (like variables.qMyQuery.myTextColumn[1]) or you wrap it up in a JavaCast like Javacast('string',variables.qMyQuery.myTextColumn).
I finally found a moment to actually do some benchmarking on some of the built-in ColdFusion functions against their Java counterparts. This is not a benchmark of Java vs. ColdFusion performance, mind you, it's about deciding whether to use Java-methods inside of ColdFusion vs. ColdFusion's built-in string functions.
The test subject here is a string of 2848 characters length, pulled from our live article database, which should provide a realistic use case. The JavaCast has already been done, so if we're working with string data that may not be represented by actual Java string objects at the time of access, we may need to account for some additional processing cycles to do the JavaCast in order to be able to use Java methods on it. I ran the tests several times on the same machine, making sure the results where actually reproducable on each run. The tests have been performed on ColdFusion 8 Enterprise running in a 64-bit multi-instance configuration. If you're running CF9, your mileage may vary, though I wouldn't expect the results to point in a very much different direction.
On to the tests.
len() vs. length()
<cftimer type="outline" label="coldfusion len"> <cfloop from="1" to="10000" index="variables.i"> <cfset variables.iCfLen = len(variables.strText) /> </cfloop> </cftimer> <cfoutput>#variables.iCfLen#</cfoutput> <cftimer type="outline" label="java length"> <cfloop from="1" to="10000" index="variables.i"> <cfset variables.iJLength = variables.strText.length() /> </cfloop> </cftimer> <cfoutput>#variables.iJLength#</cfoutput>
Results: ColdFusion 76ms vs. Java 104ms.
Find() vs. indexOf()
<cftimer type="outline" label="coldfusion find"> <cfloop from="1" to="10000" index="variables.i"> <cfset variables.iCfFind = Find('Singularity',variables.strText) /> </cfloop> </cftimer> <cfoutput>#variables.iCfFind#</cfoutput> <cftimer type="outline" label="java indexOf"> <cfloop from="1" to="10000" index="variables.i"> <cfset variables.iJindexOf = variables.strText.indexOf('Singularity') /> </cfloop> </cftimer> <cfoutput>#variables.iJindexOf+1#</cfoutput>
Results: ColdFusion 67ms vs. Java 115ms.
& vs. concat()
<cftimer type="outline" label="coldfusion &"> <cfloop from="1" to="10000" index="variables.i"> <cfset variables.strCFconcat = variables.strText & variables.strText/> </cfloop> </cftimer> <cftimer type="outline" label="java concat()"> <cfloop from="1" to="10000" index="variables.i"> <cfset variables.strJconcat = variables.strText.concat(variables.strText) /> </cfloop> </cftimer>
Results: ColdFusion 120ms vs. Java 172ms.
ReReplace() vs. replaceFirst()
<cftimer type="outline" label="coldfusion ReReplace"> <cfloop from="1" to="10000" index="variables.i"> <cfset variables.strCfReReplace = ReReplace(variables.strText,'Singularity','Singularity 2', 'ONE') /> </cfloop> </cftimer> <cftimer type="outline" label="java replaceFirst"> <cfloop from="1" to="10000" index="variables.i"> <cfset variables.strJreplaceFirst = variables.strText.replaceFirst('Singularity','Singularity 2') /> </cfloop> </cftimer>
Results: ColdFusion 331ms vs. Java 228ms.
ReReplace() vs. replaceAll()
<cftimer type="outline" label="coldfusion ReReplace"> <cfloop from="1" to="10000" index="variables.i"> <cfset variables.strCfReReplace = ReReplace(variables.strText,'Singularity','Singularity 2', 'ALL') /> </cfloop> </cftimer> <cftimer type="outline" label="java replaceAll"> <cfloop from="1" to="10000" index="variables.i"> <cfset variables.strJreplaceFirst = variables.strText.replaceAll('Singularity','Singularity 2') /> </cfloop> </cftimer>
Results: ColdFusion 543ms vs. Java 629ms.
Conclusion
There seems to be a certain overhead involved in using the underlying Java methods instead of the build-in ColdFusion functions (or operators in case of the &). The difference is actually not that much, and depending on the data you work on, the Java approach might actually gain the upper hand in some cases.
However, based on these findings I would suggest you stick with ColdFusion for such simple cases; your CFML code will be more independent of the underlying VM (which may even be .NET) and easier to write, too, as most ColdFusion IDEs will not support mixed code completion for both CFML and Java.
This is no reason not to take a look behind the curtain, though. Where Java really shines from a ColdFusion developers view is in solving problems that couldn't be as elegantly solved in plain CFML. For example, you'll be able to implement a buffered stream writer instead of <cffile action="append" />, you'll have java.util.Calendar to do your bidding working with date/time-data, and you'll be able to use a wide range of external Java libraries from within ColdFusion, solving both trivial and most complex problems without you doing all the legwork coding the solutions.
On the other hand, when you actually have discovered some of the gems that are hidden in the Java foundation of ColdFusion, don't just blindly adopt everything you find there. You still need to do some testing to find the tool that fits your needs best.
