Explain Codes LogoExplain Codes Logo

Compare two objects in Java with possible null values

java
null-safety
equals
comparator
Alex KataevbyAlex Kataev·Aug 14, 2024
TLDR

Leverage the power of Java's Objects.equals(Object a, Object b) for null-safe object comparisons. Here's how you use it:

boolean areEqual = Objects.equals(obj1, obj2);

It's your one-stop solution for handling null values, equating two null objects as true, and a null with a non-null object as false, all without the dreaded NullPointerException.

Legacy code: Handling null comparison

Working with Java versions prior to 7? No problem. You can implement a replica of Objects.equals:

public static boolean equals(Object a, Object b) { // They're the same... like twins if (a == b) return true; // One of them doesn't exist, NPE disaster averted if (a == null || b == null) return false; // Alright, time to check if they are really, really equal return a.equals(b); }

This will mimic Objects.equals giving you a null-safe comparison.

Beware of null String traps!

Though Objects.equals is great, string comparison with null awareness requires special attention. Apache Commons Lang has StringUtils.equals to deal with these:

boolean areEqual = StringUtils.equals(str1, str2); // null Strings can't trick us anymore!

For Android enthusiasts, TextUtils.equals is just the tool for you:

boolean areEqual = TextUtils.equals(str1, str2); // Android, we got you covered!

Embrace nulls in sorting

When you play with collections and sorting, you might encounter null values. Here's how to create a null-safe comparator:

Comparator<String> nullSafeComparator = Comparator.nullsFirst(String::compareTo); // nulls aren't the enemy

Complex objects? No problem! Chain your comparisons with Comparator's thenComparing:

Comparator<Person> comparator = Comparator .comparing(Person::getName, Comparator.nullsFirst(String::compareTo)) .thenComparing(Person::getAge); // first name first, age next!

Be vigilant: Watch out for these cases

Objects.equals is handy but not a magic wand. Be alert for special cases:

  • Custom objects: Overridden equals method? If not, do it now!
  • Primitive types: Objects.equals won't play nice, use wrappers or direct comparisons.
  • Large collections: Beware the performance! Avoid unnecessary comparisons.

Common errors that will cost you

Common bugs to squash when comparing objects:

  1. equals contract violation: Should be reflective, symmetric, transitive, and consistent.
  2. Forgotten about hashCode: No equals without hashCode, maintain their contract.
  3. Comparing apples and oranges: Different types yield false.
  4. Sorted collections: Using custom comparator can alter the location and presence of null values.

Learn from the masters: Source code

Dive into StringUtils.equals's code for null-safe strings comparison:

public static boolean equals(final CharSequence cs1, final CharSequence cs2) { // Source code simplified if (cs1 == cs2) { // Are they the same thing? return true; } if (cs1 == null || cs2 == null) { // The non-existent can't be equal to something! return false; } // Same length? If not, they can't be equal if (cs1.length() != cs2.length()) { return false; } // Finally, let's compare the Strings! if (cs1 instanceof String && cs2 instanceof String) { return cs1.equals(cs2); } // More logic here }