Micro optimizations in Java. String.equals()

Dmytro Dumanskiy
Javarevisited
Published in
5 min readJul 28, 2020

Hello everybody, my name is Dmytro Dumanskiy, I’m a CTO and Co-Founder at Blynk, an Internet of Things platform for businesses. We work hard on making it simple for businesses to connect their electronic devices to the Internet and build white-label apps to monitor and control them remotely. This is my first article in English, so please excuse me for any mistakes.

In the early days of our product, we had to optimize our back-end infrastructure, again and again, to spend less on the servers. Here is a short background of the loads we are working with right now in our public cloud:

  • ~80k requests per seconds (~8k req-sec per CPU with 40% utilization)
  • ~50k hardware connected to the cloud every second
  • ~350k MAU
  • 150$ monthly maintenance cost

With this post, I’m starting a series of articles dedicated to Java micro optimizations. By micro optimizations I mean small, almost invisible, tiny changes within popular Java code constructions that affect the performance of your code.

In most cases, micro optimizations won’t get you a whole lot of performance boost (you should focus on architecture, algorithms, and data structures for that in the first place). However, in some hot code sections, you can get visible performance improvements. I’ll keep my examples as simple as possible and my posts as short as possible with minimum blah-blah so you don’t get bored.

I believe this article will be useful for every Java developer.

So, let’s get started.

(Please consider all the below code from the point of performance)

(Please do not focus on numbers, this is just a metric to prove the point)

Empty String

Let’s look at this basic code:

if (s != null && s.equals(“”)) {}

or another “tricky” code to avoid null check:

if (“”.equals(s)) {}

What is wrong here?

At first glance, it looks like a correct code. However, if we think about performance, it is not the best choice. Let’s change our approach a bit and use the String.isEmpty() method instead :

if (s != null && s.isEmpty()) {}

Let’s check it with a JMH benchmark:

Results (lower score means faster):

OpenJDK Runtime Environment (build 11.0.7+10-post-Ubuntu-2ubuntu218.04)

Now, our new code is ~20–40% faster depending on the input string param.

It is not hard to see where this difference in performance comes from after we look at the String.equals() method:

Operator instanceOf, class casting, 3 if statements, and all this is just to start the check if the string is actually empty. Now compare it with String.isEmpty():

public boolean isEmpty() {    return value.length == 0;}

Actually, we don’t need a benchmark here to prove isEmpty() is faster, but it’s interesting to understand just how much faster it could be — 1%, 10%, 100%?

What do these results tell us? Always use isEmpty() instead of “”.equals(). It will save you a few CPU cycles. This change is so simple! Also, there is one more, less obvious benefit. isEmpty() generates less byte code, so your compiler has more options for inlining. That’s always good for performance.

In some cases, the input string could be interned and the “”.equals(s) performance would be similar to the isEmpty() method when s == “”. However, in real-life applications, this is not the case due to non controlled input.

“Okay, I’m an experienced engineer and have been using isEmpty() since it was added in Java 6. My code is fast”, you would say. True, but also you’re using dozens of different libraries and frameworks.

Let’s look at one of the most popular ones, the Spring framework:

Or Hibernate:

Or IntelliJ Idea, where these screenshots are from:

You got the point… And this is in the top frameworks with hundreds of contributors. I wonder how many of these you have in your own code.

One char equals

Now, let’s consider a slightly different example. In case you are or have been working with web servers, you must have seen these lines of code:

“/”.equals(url)

or

url.equals(“/”)

What is wrong here?

I bet you can find this code on every web server. Here it is in Spring-web:

The problem here is the same as in the above example, specifically the String.equals() method. It is too generic. And we need only one char check. Unfortunately, the String class doesn’t have the equals(char c) method, so we have to deal with the string.

Okay, so what can we do? Let’s make a simple, clear, and specialized version of the equals(String s) but for one char:

Results (lower score means faster):

So, what do we have here? A 50% better performance for an “/” string and a 10% better performance for the other strings. Not much, but still faster.

The point here is that typical web servers have to handle this check on every request.

I know, for many of you these examples are super obvious. However, I see these problems in every library I use. It’s time to fix that.

Phew… That’s it for now. Hope you like it and it’s helpful.

Conclusion

  • Always use String.isEmpty() instead of “”.equals(s) or s.equals(“”)
  • For hot paths use a specialized version of String.equals(String s) - equals(char c)

Source code of benchmarks, so you can try it yourself.

Thank you for your attention and stay tuned.

P. S. By the way, in case you wanted to contribute to open-source projects and never had a good starting point — this is it. A simple change that can speed up millions of servers out there, even if only by a tiny bit.

Then next article is ready — Micro optimizations in Java. String.equalsIgnoreCase().

--

--