The title says it all. My question regards the efficiency of different String
equivalency methods. I frequently use .equalsIgnoreCase(String str)
because I just have a thing for it. But I'm beginning to wonder if it may not be the most efficient method for finding equivalency between Strings
. It seems to me that .equalsIgnoreCase(String str)
is calling one of the case shifting methods toUpperCase
or toLowerCase
then calling equals
in its definition, but I am likely wrong. So, which of these methods is more efficient in the following situation, or any situation for that matter?
int count = 0;//checks for face cards at indexes listed in selectedCards
// Selected cards is Integer ArrayList
for(; (count < selectedCards.size() && count < 3); count++)
{
if(cardAt(selectedCards.get(count)).rank().equalsIgnoreCase("Queen"))
count++;
else if(cardAt(selectedCards.get(count)).rank().equalsIgnoreCase("King"))
count++;
if(cardAt(selectedCards.get(count)).rank().equalsIgnoreCase("Jack"))
count++;
}
if(count == 3)
return true;
return false;
equalsIgnoreCase() is 20–50% faster than the equals(param. toLowerCase()) pattern. And 25 times faster when the parameter doesn't match the constant string.
Difference between equals() vs equalsIgnoreCase() in JavaUse equals() in Java to check for equality between two strings. Use equalsIgnoreCase() in Java to check for equality between two strings ignoring the case.
The equalsIgnoreCase() method compares two strings, ignoring lower case and upper case differences. This method returns true if the strings are equal, and false if not.
JMH makes microbenchmarking easy:
Update: Set up the input string as a parameter to address the comment from JMH God Alexey Shipilev. I left the target string constant because OP's use case is to compare input strings to a constant.
@State(Benchmark)
public class StrComp {
@Param({"Queen", "queen", "King"})
public String input;
@Benchmark
public boolean eqIgnoreCase() {
return input.equalsIgnoreCase("queen");
}
@Benchmark
public boolean eqToLower() {
return input.toLowerCase().equals("queen");
}
public static void main(String[] args) throws RunnerException {
Options opt = new OptionsBuilder()
.include(".*StrComp.*")
.mode(Mode.AverageTime)
.timeUnit(TimeUnit.NANOSECONDS)
.forks(5)
.warmupIterations(10)
.measurementIterations(10)
.build();
new Runner(opt).run();
}
}
And the output:
Benchmark Mode Cnt Score Error Units
StrComp.eqIgnoreCase avgt 50 18.581 ± 0.051 ns/op
StrComp.eqToLower avgt 50 54.796 ± 0.173 ns/op
Updated output with parameter:
Benchmark (input) Mode Cnt Score Error Units
StrComp.eqIgnoreCase Queen avgt 50 17.947 ± 0.205 ns/op
StrComp.eqIgnoreCase queen avgt 50 15.553 ± 0.159 ns/op
StrComp.eqIgnoreCase King avgt 50 2.968 ± 0.037 ns/op
StrComp.eqToLower Queen avgt 50 56.499 ± 0.180 ns/op
StrComp.eqToLower queen avgt 50 22.023 ± 0.040 ns/op
StrComp.eqToLower King avgt 50 49.174 ± 0.145 ns/op
So, eqIgnoreCase is faster but unless you are doing a million comparisons per second, you won't notice any difference.
You can play around and see how the difference would be affected if the first string is already lower case or if the strings are of different lengths, etc.
Anyway, If you want to make your code more "efficient" and also more clear, type-safe and less prone to bugs, don't use strings for things like this. Use enums.
Deck of cards is so well-suited to implementation by enums, it's frequently used to illustrate the enum
concept: http://docs.oracle.com/javase/8/docs/technotes/guides/language/enums.html#Card
Surprisingly, these methods are not equivalent for some strange Unicode reasons:
toUpperCase("ß")
returns "SS"
, i.e., two letters, while equalsIgnoreCase
works character-wisetoUpperCase
nor toLowerCase
is sufficient, you must do bothConcerning efficiency, I'd bet that equalsIgnoreCase
is way faster, as it doesn't copy any data. It also starts with the length comparison.
Note also that toUpperCase
and toLowerCase
are Locale
-sensitive, while equalsIgnoreCase
is not. IIRC the performance diminishes if an exotic locale gets used.
The best and simplest optimization would be to normalize the casing beforehand. There's no reason to have a "Queen", a "queen", and a "QueEN" in your data - clean up the input ASAP.
You can also use an enum
for representing the rank.
I'm afraid, your loop is broken. After any of JQK you skip one card, is that intended???
Do the following
if (x) return true; else return false;
by return x;
The code without using enum
s and early normalization would look like this:
int count = 0;
// Whatever Position is
for (Position p : selectedCards) {
String rank = cardAt(p).rank();
if (rank.equalsIgnoreCase("Jack")
|| rank.equalsIgnoreCase("Queen")
|| rank.equalsIgnoreCase("King")) {
++count;
if (count > 3) { // Tiny and probably useless optimization.
return false;
}
}
return count == 3;
You can check the code simply yourself (it is included in SDK).
The .equalsIgnoreCase is faster than toUperCase().equals() In java 8, the code is:
while (len-- > 0) {
char c1 = ta[to++];
char c2 = pa[po++];
if (c1 == c2) {
continue;
}
if (ignoreCase) {
// If characters don't match but case may be ignored,
// try converting both characters to uppercase.
// If the results match, then the comparison scan should
// continue.
char u1 = Character.toUpperCase(c1);
char u2 = Character.toUpperCase(c2);
if (u1 == u2) {
continue;
}
// Unfortunately, conversion to uppercase does not work properly
// for the Georgian alphabet, which has strange rules about case
// conversion. So we need to make one last check before
// exiting.
if (Character.toLowerCase(u1) == Character.toLowerCase(u2)) {
continue;
}
}
return false;
}
return true;
so if the compared string are actually different it will be faster than first calling toUpderCase then equals as the toUperCase will modifiy all the characters. Also the logic involved in toUperCase seems to be more complicated than in the comparison loop and it creates a new String object at the end.
But you would need to have A LOT of comparison operations to actually see any difference.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With