41
if ('text'.equals(str))

or

if ('text' == str)

Is there any difference? If yes, then what is it?



Added, after answered.

String q = 'teXt';
system.debug('teXt'.equals(q)); // true
system.debug('teXt' == q);      // true
system.debug('text'.equals(q)); // false
system.debug('text' == q);      // true

4 Answers 4

49

Yes there is a major difference, Case sensitivity.

https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_methods_system_string.htm

> equals(stringOrId) Returns true if the passed-in object is not null and represents the same binary sequence of characters as the current string. Use this method to compare a string to an object that represents a string or an ID.

== is same as equalsIgnoreCase(secondString)

Returns true if the secondString is not null and represents the same sequence of characters as the String that called the method, ignoring case.

3
  • 1
    Hi, is there any doc that says that "== is the same as equalsIgnoreCase"? If so, please add it to your post. Cheers Jun 20, 2019 at 10:08
  • 1
    Unfortunately, your answer is incorrect. I'll be adding more details, it's a bit more subtle.
    – sfdcfox
    Apr 30 at 3:50
  • For any developers coming from a Java background, I would like to point out that string comparison in Apex is different from Java. Even if the operators look the same. The use of == is discouraged in Java, because it compares equality of objects (not the String content), which is most of the times not what you want. In Apex however, the use of == seems to be recommened, because it works as expected. May 12 at 11:43
35

I would add that == is more null safe. Consider the following examples.

String a;
String b = null;
system.assert(a == b);

This will pass.

String a;
String b = null;
system.assert(a.equals(b));

This will throw a null pointer exception.

The above demonstration is why I tend to prefer == over .equals. If you are going to use .equals, you should always first check that it is not null, though it is not necessary when using constants.

final String SOME_CONSTANT = 'abc';
...
if (SOME_CONSTANT.equals('abc'))
{
    // do stuff
}
if (someParameter == SOME_CONSTANT)
{
    // do other stuff
}

Also note that while .equals is case sensitive, you can still call .equalsIgnoreCase.

In addition to what I've already stated, I did some informal profiling and it seems == may be significantly faster than .equals. More than 7 times as fast.

.equals

String a = 'abc';
String b = 'abc';
DateTime start = Datetime.now();
for (Integer i = 0; i < 100000; i++) Boolean isEqual = a.equals(b);
Decimal interval = Datetime.now().getTime() - start.getTime();
system.debug(interval); // yields 1831

.equalsIgnoreCase

String a = 'abc';
String b = 'abc';
DateTime start = Datetime.now();
for (Integer i = 0; i < 100000; i++) Boolean isEqual = a.equalsIgnoreCase(b);
Decimal interval = Datetime.now().getTime() - start.getTime();
system.debug(interval); // yields 1514

==

String a = 'abc';
String b = 'abc';
DateTime start = Datetime.now();
for (Integer i = 0; i < 100000; i++) Boolean isEqual = a == b;
Decimal interval = Datetime.now().getTime() - start.getTime();
system.debug(interval); // yields 239

Even the slowest of these is consuming less than 1/5 ms per execution, but if you get in a big loop and your calls start to number in the millions, that can add up.

2
  • 5
    Good point about performance. Will keep this in mind. Thanks! Jun 20, 2015 at 12:41
  • Calling methods in 2015 was expensive, which is why your short string tests worked out the way they did. It would also have been further affected by your debug log levels (calling methods makes more log entries). For larger strings, you'd find that the opposite is true, equalsIgnoreCase will dominate == in some scenarios. I just today found out that == and equalsIgnoreCase are two different functions.
    – sfdcfox
    Apr 30 at 4:44
4

Side note:My actual comment starts below in the next paragraph, I tried to add just a comment to the answer above me related to the statements about the speed of the == versus .equals() methods but I need 50 points to comment yet I'm allowed to answer, even though my answer which is related to the other answer would be much better as an 'in thread' comment. Regardless....

As Adrian I'm sure knows, you need to be very careful when doing performance comparisons as a lot of things can affect the timing, from methods not being 'warmed' up, to incorrectly setup performance tests, to timing the wrong thing and inadvertently timing other functionality beyond what you thought.

As an example I ran the exact same code above (for the .equals()/.equalsIgnoreCase()/== timings) a total of 50 times to try and get an 'average' value (for some form of average). After 50 runs of each of the 3 tests and with each doing 100,000 loops per for loop I ended up with times for the .equals() and .equalsIgnoreCase() methods that are within a few percent of each other so for simplicity here I'm treating them as the same and showing the average value for them.

.equals/.equalsIgnoreCase = ~815ms (ranging in tests from 650 to 909)
using the 2equal signs == = ~467ms (ranging in tests from 350 to 838)

So the performance timings I get (after a total 50 test runs) indicates about a roughly 75% improvement using the == over the .equalsIgnoreCase() however that is a long way away from the 700%/7x mentioned above.

Also, I've put the min and max times that I got next to each of the times as well as this just goes to show how inaccurate/variable the performance timing testing can be. Although there was roughly a 75% improvement on average using '==' in some tests the times could vary so much that it would actually be slower than the .equalsIgnoreCase() methods.

Further, I was running 5 lots of the 3 tests each time and collecting 5 lots of 3 sets of values after every run. If there was load on my laptop during these test runs it could greatly effect the times and cause a time on one/many of the test cases to blow out greatly. To try to minimise this I would have no other apps/programs/tools running/processing at the same time and once the test was started/running I would not move the mouse, click, perform any kind of input until the test completed. I also decided that as part of my methodology that if even a single test case blew out in terms of time because of load then I would completely exclude ALL 5 sets of 3 test results in that run and re-run. In some of these test runs where there was load on the laptop during the test runs I would be getting times of 1000, 2000, 3000ms and more which was multiple times above the final averages.

So, to sum up, what I'm trying to get at is that performance figures need to be carefully obtained and handed up with a large number of caveats usually. I do agree with Adrian in that the '==' method of comparison of Strings in Apex is actually quicker although perhaps no where near 7 times faster but somewhere closer to less than twice as fast. It also gives you null safety in situations like:

String a = null;
String b = 'foo';
System.debug('       a==b =' + (a==b));                  // false
System.debug('a.equals(b) =' + (a.equalsIgnoreCase(b))); // Null Pointer Exception
System.debug('a?.equals(b)=' + (a?.equalsIgnoreCase(b)));// null
0

==, ===, equals and equalsIgnoreCase are all slightly different. I'll go over all of these in more detail. tl;dr at bottom.

===

You're not allowed to directly use this on String variables, but cast to an Object, and you'll get reference comparisons. This works because Apex borrows the idea of Java's "string pool", where a duplicate string from the pool just returns the reference. This is a heap memory trick to reduce memory usage in a program. Since strings are immutable in memory (any mutation causes a new string to be created in the pool), this is perfectly safe. It's also the fastest of the comparison options we have, but is strictly case-sensitive and locale-agnostic.

Object v1 = 'hello world';
Object v2 = 'hello world';
System.debug(v1 === v2); // true

equals

This method first checks the length of the two strings, and if they match, proceeds to check each character one at a time until a mismatch or the strings are fully compared. For equal strings, this is just about as fast as ===. Even on a non-match in a very large string (say, 1,000,000 characters), this is still only about twice as slow as ===. It's a good choice for case-sensitive, locale-agnostic comparisons. Just be aware that the variable equals is called from must not be null, or you'll throw an exception. If you're not sure, check for null first, or use the Safe Navigation Operator (?.).

String v1 = 'hello world';
String v2 = 'hello world';
System.debug(v1.equals(v2)); // true
System.debug(v1?.equals(v2) == true); // true, use if you need null safety

equalsIgnoreCase

We use this when we want case-insensitive, locale-agnostic string comparisons. It's roughly the same speed as v1.toLowerCase().equals(v2.toLowerCase()) (or toUpperCase). That is, it's far slower than either === or equals, but much, much faster than == in some scenarios. Note that because of a lack of locale awareness, it can produce false positives or false negatives. This is only possible in a handful of languages where certain characters between cases differ. It's a concept that I find challenging to explain, as I don't have any concrete examples, nor am I a linguistics expert. Just make sure you test some expected inputs to see if you get any weird results.

String v1 = 'hello world';
String v2 = 'HELLO WORLD';
System.debug(v1.equalsIgnoreCase(v2)); // true
System.debug(v1?.equalsIgnoreCase(v2) == true); // true, use if you need null 

==

The default operator, which in the documentation is merely described as case-insensitive matching, is actually a locale-aware case-insensitive comparison. It is roughly the equivalent of calling v1.toLowerCase( UserInfo.getLocale() ).equals( v2.toLowerCase( UserInfo.getLocale() ) ). Enabling locale-awareness comes at considerable cost in CPU time.

If you need locale-aware, case-insensitive matching, you'd be better off converting to a common casing and using ===:

String userLocale = UserInfo.getLocale();
Boolean isMatch = (Object)(v1?.toLowerCase(userLocale)) === (Object)(v2?.toLowerCase(userLocale));

This should be as accurate as == is, but provide much better performance with large strings.


tl;dr

We have four ways to check equality: two case-sensitive models, one locale-agnostic case-insensitive model, and one locale-aware case-sensitive model. == is the most technically accurate comparison, but has the worst potential performance; it performs better than equalsIgnoreCase on small strings, but suffers on large strings. equalsIgnoreCase is less technically accurate, but has far better performance compared to == if you need to deal with strings of large sizes. If you're not sure which one you need, just stick with ==. It'll probably be okay.

If you start to notice high CPU usage, consider if locale-awareness is worth it. If not, you can go with equalsIgnoreCase or (Object)toLowerCase === (Object)toLowerCase, and if so, you can switch to (Object)toLowerCase(userLocale) === (Object)toLowerCase(userLocale) (see examples, above).

The reason why == beats .equalsIgnoreCase on small strings is that calling methods involves the using the call stack, which has a very small, but non-zero, CPU penalty, but once the strings get large enough, == performance problems will become more apparent.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.