Thursday, June 25, 2026

Always compare objects (including Integer and Long etc) with equals

 The difference between equals() and == in Java is known to everyone. There is a minute detail, though, which I will explain a moment later.

Consider the situation:

Map<String, Integer> map = new HashMap<>();
map.put("a",128);
map.put("b",128);

map.put("c",1100);
map.put("d",1100);

map.put("e",5);
map.put("f",5);

System.out.println(map.get("a") == map.get("b"));
System.out.println(map.get("c") == map.get("d"));
System.out.println(map.get("e") == map.get("f"));

The first two return false, while the third one returns true.
This means that if you depend on ==  for equality comparison of two wrapper objects, your code can silently fail.

Why do you need to compare?
Various reasons based on your requirement. One common reason could be using them in a Comparator. Whatever be the reason, using == can fail you and your code.

Why does it fails? 
JVM caches primitives to some extent. For example, int and long values -128 to 127 are cached. The range is decided by the writers of JLS (Java Language Specification) to improve performance by avoiding repeated creation and garbage collection of objects for common use cases such as loops, basic counts or array indexing. Similarly, the booleans true and false are cached too.

When you use java.lang.Integer, or do boxing such as in the map values above, new Object is created which resides on the heap. Comparing them means comparing object. If the value falls within the cached values, JVM object simply references or points to the cache object.
So when you have Integer a = 128; JVM creates a new Object on the heap. When you have Integer b = 120, JVM find the value in the cache and just points to that, preventing a new Object to be managed.
When you use primitives, int a = 128, this is just a member of the thread stack or sits inside the object of the class where its defined. 

Now see below:
Integer a = 130, b = 130;
System.out.println(a==b); // false
System.out.println(a==130); // true

First one gives false since objects are compared, and both are different by virtue of their hashcodes even if their content is same.
Second line gives true because upon seeing the ==, JVM just unboxes the "a" to primitive 130 and quickly compares.

An actual code:

Map<String, Integer> map = new HashMap<>();
map.put("a", 130);
map.put("b", 120);
map.put("c", 130);
map.put("e", 140);
map.put("f", 150);

        List<String> sortedList = new ArrayList<>();
sortedList.addAll(map.keySet());
Comparator<Integer> comparator = (str1, str2) -> {
// reverse sort on same values
if(map.get(str1) == map.get(str2)) {
return str2.compareTo(str1);
}
// normal sort on different values
return Integer.compare(map.get(str1), map.get(str2));
};
        Collections.sort(sortedList, comparator);

The expectation is that the list should be sorted based on the below conditions:
1. If count is the same for multiple strings, sort them in reverse order
2. If the counts of two strings are not same, sort them in a normal ascending or lexicographical order.

Our expected output should be : [b, c, a, e, f].
Notice how "a" and "c" are in reverse order.

But if you run the above code, you will get: [b, a, c, e, f].
"a" and "c" never were reverse sorted because the == actually compared two different objects with different hash code, ensuring our goal was not met. 
Were we having some other strings with the same count within the cache-able range, i.e. -128 to 127, this == would have worked flawlessly.
This means our code is now flaky with == comparison of two Objects. Only way it will work as expected is to use equals():

            if(map.get(str1).equals(map.get(str2))) {
return str2.compareTo(str1);
}
On paper and IDE, == looked safe until it silently failed.

Conclusion:
The most important thing to remember is not to depend on things that confuse or feel flaky, strictly use whats advised: == for primitives and .equals for objects (wrapper classes like Integer, Long are objects after all).

Thursday, October 25, 2018

Git: Resolve Conflicts in PR

What to do when GitHub does not allows you to merge a Pull Request due to conflicts!
First of all, rebase often to avoid this problem in most cases.
Sharing bits from my experience. The solutions that worked for me are:

1. git rebase

The best way is to do it locally using terminal or command prompt. Lets say you raised a PR from origin/develop to upstream/develop branch and got conflict. Go to your command prompt, rebase master onto develop and push to develop branch. PR should get resolved.
But this won’t work when the tips are just big time outdated.

Wednesday, August 16, 2017

Using Wiremock for quick and easy http mocks of your API

I published this article at the turn of the year in my company's internal blog. I am republishing it here thinking it might be useful to outside world who are looking at a concise guide.

WireMock is an HTTP mock server. At its core it is web server that can be primed to
  1. serve canned responses to particular requests (stubbing) and 
  2. that captures incoming requests so that they can be checked later (verification).
Imagine your application component is dependent on some other component for development or testing, here WireMock can come in and remove your dependency headaches.
You can agree the contract, design the stub and use it for your own component without tight integration with other component. This makes the development and testing much
easier and faster.

Monday, February 20, 2017

Stackoverflow Documentation | The way to go

Many months now, the Stackoverflow documentation is maturing with new examples and continuous edits to existing examples. It covers many programming languages, and should be on top of your TO-DOs if you are trying to broaden your skill set or even validate what you already have in your kitty.

If you feel it's good, you should consider making it awesome for others. Ultimately, giving back to the community only makes sure of technology's global reach and rapid development. Click here: http://stackoverflow.com/documentation.

Thursday, September 1, 2016

Recursion is all about trust.

Image credit: http://www.cr31.co.uk/logoarts/
The secret of recursion is only one thing. Trust. Here's a neat example to show you what it exactly means. Quoting Stephan van Hulst from Code Ranch here.

Say there's a long queue of people, and you want to know how many are in the queue. You can simply ask the guy in front of you how many people there are in the queue, and then add 1 for yourself. You don't care how the guy in front of you got the answer, you just trust that it's correct. The guy in front uses the same technique. This goes on all the way until the guy at the front of the queue is asked how many people there are in the queue. The guy at the front sees no people in front of him, so he just reports 1. He is the base case.

final class PersonInQueue {
 
  private final PersonInQueue next;
 
  int askForLengthOfQueue() {
    if (next == null)
      return 1;
 
    return next.askForLengthOfQueue() +1;
  }
}
Here's the post: Algorithm explanation if you want to have a look. The question was on Tower of Hanoi.