ColdFusion String Comparison: Compare() vs. Equals() vs. CompareTo()

Posted September 5, 2006 at 1:12 PM by Ben Nadel

Tags: ColdFusion

As part of my on-going study of the Java that lives under ColdFusion, I thought I would test out some string comparison methods so see if going directly to the Java methods would be any faster than using the ColdFusion methods. For this, I compared the following:

  1. Compare() - Built into ColdFusion
  2. String::CompareTo() - Built into Java string
  3. String::Equals() - Built into Java string
  4. String::EqualsIgnoreCase() - Built into Java string

Compare( strOne, strTwo )

Compare takes two strings and does what I assume is some sort of Bit manipulation to figure out if two strings are equal. If the two strings are equal, it returns zero. If the first string is "less than" the second string, it returns -1. If the first string is "greater than" the first, it returns 1.

String::CompareTo( strOne | objOne )

The Java string method CompareTo takes either a string or an object and tests for equality. I am not 100% sure, but I believe the return values to be that of the ColdFusion Compare method.

String::Equals( objOne )

The Java string method Equals takes an object and compares it to the string. This returns a boolean true/false.

String::EqualsIgnoreCase( strOne )

The Java string method EqualsIgnoreCase takes a string object and compares it to the string ignoring case. This returns a boolean true/false.

Now, for the test:

  • <!---
  • Start off with a test statement. This is the string to
  • which we will be comparing dynamic strings.
  • --->
  • <cfset strStatement = "I like girls that weigh 165 lbs." />
  •  
  • <!--- Set the number of iterations for testing. --->
  • <cfset intIterations = 5000 />
  •  
  •  
  • <!--- Test the ColdFusion Compare() method. --->
  • <cftimer label="NOT Compare" type="outline">
  •  
  • <!--- Loop for speed testing. --->
  • <cfloop index="intI" from="1" to="#intIterations#" step="1">
  •  
  • <!--- Create random test string. --->
  • <cfset strTest = (
  • "I like girls that weigh " &
  • NumberFormat( intI, "000" ) &
  • " lbs."
  • ) />
  •  
  • <!--- Check to see if the statements match up. --->
  • #YesNoFormat(
  • NOT Compare( strStatement, strTest )
  • )#
  •  
  • </cfloop>
  •  
  • </cftimer>
  •  
  •  
  • <!--- Test the Java String::CompareTo() method. --->
  • <cftimer label="CompareTo" type="outline">
  •  
  • <!--- Loop for speed testing. --->
  • <cfloop index="intI" from="1" to="#intIterations#" step="1">
  •  
  • <!--- Create random test string. --->
  • <cfset strTest = (
  • "I like girls that weigh " &
  • NumberFormat( intI, "000" ) &
  • " lbs."
  • ) />
  •  
  • <!--- Check to see if the statements match up. --->
  • #YesNoFormat(
  • strStatement.CompareTo( strTest )
  • )#
  •  
  • </cfloop>
  •  
  • </cftimer>
  •  
  •  
  • <!--- Test the Java String::Equals() method. --->
  • <cftimer label="Equals" type="outline">
  •  
  • <!--- Loop for speed testing. --->
  • <cfloop index="intI" from="1" to="#intIterations#" step="1">
  •  
  • <!--- Create random test string. --->
  • <cfset strTest = (
  • "I like girls that weigh " &
  • NumberFormat( intI, "000" ) &
  • " lbs."
  • ) />
  •  
  • <!--- Check to see if the statements match up. --->
  • #YesNoFormat(
  • strStatement.Equals( strTest )
  • )#
  •  
  • </cfloop>
  •  
  • </cftimer>
  •  
  •  
  • <!--- Test the Java String::EqualsIgnoreCase() method. --->
  • <cftimer label="EqualsIgnoreCase" type="outline">
  •  
  • <!--- Loop for speed testing. --->
  • <cfloop index="intI" from="1" to="#intIterations#" step="1">
  •  
  • <!--- Create random test string. --->
  • <cfset strTest = (
  • "I like girls that weigh " &
  • NumberFormat( intI, "000" ) &
  • " lbs."
  • ) />
  •  
  • <!--- Check to see if the statements match up. --->
  • #YesNoFormat(
  • strStatement.EqualsIgnoreCase( strTest )
  • )#
  •  
  • </cfloop>
  •  
  • </cftimer>

I was surprised by the results. The ColdFusion Compare() method outperformed all the Java methods. Remember though, we are talking 5000 iterations here. At low iterations, they all perform lightening fast. Here is trending order of speed:

  1. Compare()
  2. String::Equals()
  3. String::EqualsIgnoreCase()
  4. String::CompareTo()

The fact that CompareTo() was the slowest and Compare() was the fastest is a bit surprising. I assume one would be built on the other. The only reason I can hazard a guess at this is because CompareTo() can take either an object or a string, and the type conversion is throwing it off. But, then again, on the other hand, Equals() takes only and object and performs well. I wanted to test EqualsNoCase() because it ONLY takes a string and I wanted to see what that did for performance. I think though, any performance boost it got from the string parameter is counter-acted by the fact that it is not case sensitive.

One reason why I wanted to test this AT ALL is the fact that seeing "NOT Compare()" for equality is not that user friendly. I was hoping that Equals() would be faster since it is nicer to look at. However, now that I think about it, I should be using JavaCast() for all of these, which adds some readability issues. I guess it was just a fun test to do.




Reader Comments

Dec 6, 2006 at 3:17 AM // reply »
1 Comments

Student loan consolidation, private student loans, free scholarship search engine, daily student loan blog, and more. - Lock in the lowest rate with NextStudent.


Jun 6, 2007 at 6:20 PM // reply »
3 Comments

Cool man thanks for taking the time to do this - I am in a project where I'm going to be manipulating strings from a db based on replace rules and I need to do a comparison once the manipulation/rules have been run on the database strings - then if the values are not the same as the origional, then I update the updated string to the db -
I was debating on what function to use and was leaning towards compare() as it was - this solidified it!


Dec 25, 2007 at 3:36 PM // reply »
132 Comments

I just stumbled across this though google and thought I'd shed some light on it. The reason the CF method is faster is because when you call a Java method in CF it uses reflection to call the method... which is quite expensive.

In fact, what CF really does is call java.lang.Class#getMethods() [1], do a search through the result to find a matching name and then try and match up the types of the arguments. It does cache the match result so subsequent calls are faster, but there's definitely overhead from doing a method call like this.

On the other hand CF's Compare function might look like this in Java:

public int Compare( String a, String b ) {
int r = a.compareTo( b );
if( r > 0 ) return 1;
return r == 0 ? 0 : -1;
}

So it should definitely be faster than doing a lookup for the method.

Things have gotten a lot faster over the years though in the Java world... [2]. :) Shame there's such a horrible class loader issue in Java 1.6 :/

[1] http://java.sun.com/j2se/1.5.0/docs/api/java/lang/Class.html#getMethods()
[2] http://www.jguru.com/faq/view.jsp?EID=246569


Dec 26, 2007 at 7:09 AM // reply »
10,640 Comments

@Elliott,

Thanks for the insight. Yeah, I have read a few really bad things about the Class Loader in Java 1.6. It seems like with CF8, a lot of people are going with Java 1.5. At least, that's what I think I've read.


Apr 1, 2009 at 10:37 AM // reply »
5 Comments

Ben,

Is there a way in Coldfusion to do a SQL like comparison?

For example, I have a webform that has a ID drop down - the iD looks something like this "AC1200" or "PR1982" etc.

Now what i wanna do is look at the first 2 characters of the ID, and based on that, enter a certain value in a column in the DB. SO that every time a "AC" appears in the ID, I should enter a specific value. Similarly, for each "PR..." entry, I want another value.

I know we can do a "WHERE Var LIKE AC%" comparison in SQL, but not totally sure if and/or how that can be done in CF!


Apr 1, 2009 at 10:43 AM // reply »
10,640 Comments

@UMTerp,

You can use REFind() for this:

<cfif REFind( "^AC", variable )> ... </cfif>

The RE = "regular expression". The "^" signifies the beginning of the string. So ^AC matches "AC" as the first part of the string.


Apr 1, 2009 at 11:28 AM // reply »
26 Comments

You can also use the Left() function to get the first 2 characters of the term - even trim it first then use Left(string,count).


Apr 1, 2009 at 11:37 AM // reply »
5 Comments

@Ben Nadel,

Great, it worked!! You got unbelievable reply speed! - within 6 mins of my query!!

Thanks!


Apr 1, 2009 at 11:38 AM // reply »
10,640 Comments

@UMTerp,

Ha ha, glad to help.

@Kevin,

Using left is good also.


May 12, 2009 at 9:36 AM // reply »
2 Comments

This is why I hate coldfusion. It doesn't hold to any conventions about how objects should be compared. For example, strings of different lengths get evaluated completely differently. I know of no other language that does this.

<cfscript>
a = '12345';
b = '12346';
// Should be false, and is false.
writeoutput('a eq b == #a eq b#<br />');

a = '2707200116272368271111';
b = '2707200116272368279465';
// Should be false, but isn't.
writeoutput('a eq b == #a eq b#<br />');
</cfscript>


May 12, 2009 at 8:02 PM // reply »
5 Comments

@Chris, Ben and others,

Re. Chris's comment about the difference in comparison results, could anyone explain why that is happening?


May 21, 2009 at 8:36 AM // reply »
10,640 Comments

@UMTerp,

My guess is that the strings that @Chris is comparing in the latter example are being converted to numbers for the comparison and the numbers that large cannot be compared well. As such, they get truncated and their truncations happen to be equal.

If you change the strings to have a preceeding, non-numeric value:

a = '_2707200116272368271111';
b = '_2707200116272368279465';

... then they are NOT equal. So, something is happen when it tries to convert them to numbers.

I've personally never had to deal with numbers this large, so it's never caused a problem. But, if you don't know why its happening, you can pull your hair out trying to debug this!


May 21, 2009 at 8:37 AM // reply »
10,640 Comments

@UMTerp,

Incidentally, if you use the Compare() function, this works with strings explicitly:

#NOT Compare( a, b )# == NO

.. which is what we expect.


May 22, 2009 at 7:20 AM // reply »
2 Comments

The most recent part of this discussion was also carried on over in the Railo group. Figure I would post a link for those interested.

http://groups.google.com/group/railo/browse_thread/thread/2b326e81c8e5e399?hl=en


Feb 24, 2011 at 1:09 PM // reply »
3 Comments

Besides speed there is something to consider when choosing a comparison method:

Today I spend hours to find a "bug" in the simple statement

// if (left (tf, j) is curr_dp) { ....
if (compare (left (tf, j), curr_dp) is 0) { ....

the simple test "is" fooled me for hours ... given these values:

curr_dp is "+43", left (tf, j) is "043"

Both are strings, curr_dp comes from a query

curr_dp = Prefix_Query["DIAL_PREFIX"][i];

and

tf is a phone number like "0431234567"

When using the if statemet with "is", it always returned true. It took me hours because I never thought of such a behavior, because both are strings (at least in my mind). Obviously one CAN see them as numerics, which makes them match.

I will never use "is" again ... :-)



Post A Comment

Comment Etiquette: Please do not post spam. Please keep the comments on-topic. Please do not post unrelated questions or large chunks of code. And, above all, please be nice to each other - we're trying to have a good conversation here.

Please review the following issues:

Author Name:


Author Email:

Author Website:

Comment:

Supported HTML tags for formatting: <strong>bold</strong>   <em>italic</em>   <code>code</code>







  • Help Wanted - Find Your Next ColdFusion Job
InVision App - Prototyping Made Beautiful With Prototyping Tools Ben Nadel's Company - Epicenter Consulting Recent Blog Comments
Feb 10, 2012 at 7:21 PM
jQuery AJAX Strips Script Tags And Inserts Them After Parent-Most Elements
Update! Instead of $(eval(options.insertAfter)).after(data['insertData']); I now use: var ajaxNode = document.createElement('span'); var parent = $(eval(options.insertAfter))[0].parentNode; ... read »
Feb 10, 2012 at 6:18 PM
jQuery AJAX Strips Script Tags And Inserts Them After Parent-Most Elements
encountered this same, what I consider, jQuery bug last week. I'm building a site in which I load some content via AJAX. This content contains Linkedin share button placeholders which Linkedin API ne ... read »
Feb 10, 2012 at 11:30 AM
Cross-Origin Resource Sharing (CORS) AJAX Requests Between jQuery And Node.js
After you understand the concepts here, this is an awesome cheatsheet for enabling CORS in just about anything http://enable-cors.org/ ... read »
JM
Feb 10, 2012 at 9:10 AM
My Safari Browser SQLite Database Hello World Example
@Amy, Here is a very good tutorial on how to use JOIN: http://www.sqltutorial.org/sqljoin-innerjoin.aspx ... read »
Feb 10, 2012 at 4:42 AM
Building A Twitter-Inspired RESTful API Architecture In ColdFusion
This is great, very useful Ben. I spotted a small typo in the api.cgm listing: <cfthrow type="Unauthroized" /> Cheers Stefan ... read »
Feb 9, 2012 at 10:35 PM
CFDirectory Filtering Uses Pipe Character For Multiple Filters (Thanks Steve Withington)
I was wondering if there would be a filter you could apply so that you got everything but what you included in the filter. As in show me all docs that are not a .pdf. ... read »
Feb 9, 2012 at 10:29 PM
Learning ColdFusion 9: Application-Specific Data Sources
@Ben, No offence, but if people were really wanting advanced features they would be using a platform like ASP.NET MVC. CFML is so structurally compromised as a tag-based scripting language that ... read »
Feb 9, 2012 at 10:03 PM
Subversion - Cleanup Failed To Process The Following Paths
@Leviaguirre, do you still have problems with this? ... read »