1. Getting started with the Objects class
Let’s get to it! If you’re tired of manually checking values for null and writing hash calculations for every field, the utility class java.util.Objects will help. Its job is to make working with objects simpler, more concise, and safer.
It really is a “Swiss Army knife”: the class can safely compare objects for equality (without risking a NullPointerException), conveniently compute hash codes, compare via a comparator, and validate arguments for null.
Objects.equals: null-safe comparison
If you just write a.equals(b) and a happens to be null, you’ll get a NullPointerException. Manual checks are verbose. Objects.equals(a, b) does it all for you:
- If both are null — returns true.
- If exactly one is null — returns false.
- If both are not null — calls the regular equals.
import java.util.Objects;
String a = null;
String b = "Java";
System.out.println(Objects.equals(a, b)); // false
String c = null;
System.out.println(Objects.equals(a, c)); // true
String d = "Java";
String e = "Java";
System.out.println(Objects.equals(d, e)); // true
Why is this convenient? The code is shorter, cleaner, and protected against accidental NPEs.
2. Objects.hash and hashCode: concise hash computation
When overriding hashCode alongside equals, it’s easy to make mistakes, especially when there are many fields. Manual code often looks verbose and brittle:
@Override
public int hashCode() {
int result = 17;
result = 31 * result + (name != null ? name.hashCode() : 0);
result = 31 * result + age;
return result;
}
The Objects.hash method fixes this—short, safe, and null-friendly:
import java.util.Objects;
public class Person {
private String name;
private int age;
// ... constructor, getters, etc.
@Override
public int hashCode() {
return Objects.hash(name, age);
}
}
Important note: Objects.hash uses varargs and allocates an array—in rare high-throughput hotspots a hand-written hashCode may be faster. For most applications the difference is negligible.
3. Objects.compare: delegate comparison to a comparator
Sometimes you need to compare two objects using a prebuilt Comparator. Instead of calling comparator.compare(a, b) directly, you can use:
int result = Objects.compare(a, b, comparator);
This method:
- Returns 0 if the objects are equal.
- Treats null as “less than” any non-null object.
- In all other cases delegates the logic to the provided comparator.
import java.util.Comparator;
import java.util.Objects;
class Person {
private String name;
Person(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
public class Main {
public static void main(String[] args) {
Person a = new Person("Anna");
Person b = new Person("Boris");
Comparator<Person> byName = Comparator.comparing(Person::getName);
System.out.println(Objects.compare(a, b, byName)); // <0, because "Anna" < "Boris"
System.out.println(Objects.compare(a, null, byName)); // >0, because a != null
System.out.println(Objects.compare(null, b, byName)); // <0, because null < b
System.out.println(Objects.compare(null, null, byName)); // 0
}
}
4. Objects.requireNonNull: insurance against “invisible” errors
If a method must accept only non-null values, check this immediately. Objects.requireNonNull will throw a NullPointerException with your message:
public void setName(String name) {
this.name = Objects.requireNonNull(name, "Name must not be null");
}
5. Example: correct implementation of equals, hashCode and compareTo with Objects
import java.util.Objects;
public class Person implements Comparable<Person> {
private String name;
private int age;
public Person(String name, int age) {
this.name = Objects.requireNonNull(name, "Name must not be null");
this.age = age;
}
public String getName() { return name; }
public int getAge() { return age; }
@Override
public boolean equals(Object o) {
if (this == o) return true; // Reference equality
if (o == null || getClass() != o.getClass()) return false;
Person person = (Person) o;
// Null-safe comparison
return age == person.age && Objects.equals(name, person.name);
}
@Override
public int hashCode() {
return Objects.hash(name, age); // Concise and safe
}
@Override
public int compareTo(Person other) {
// First compare by name, then by age
int cmp = name.compareTo(other.name);
if (cmp != 0) return cmp;
return Integer.compare(age, other.age);
}
}
Now you can store objects in a HashSet, use them as keys in a HashMap, compare them for equality, and sort lists (for example, via Collections.sort).
6. Practical use: less code and fewer bugs
Example: a user list in an application
Thanks to a correct equals/hashCode pair, searching in collections works predictably:
import java.util.ArrayList;
import java.util.List;
List<Person> users = new ArrayList<>();
users.add(new Person("Anna", 25));
users.add(new Person("Boris", 30));
Person search = new Person("Anna", 25);
System.out.println(users.contains(search)); // true
Example: working with nullable fields
If a class has fields that can be null (for example, middle name), use Objects.equals and Objects.hash:
import java.util.Objects;
public class User {
private String firstName;
private String middleName; // May be null
private String lastName;
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
User user = (User) o;
return Objects.equals(firstName, user.firstName)
&& Objects.equals(middleName, user.middleName)
&& Objects.equals(lastName, user.lastName);
}
@Override
public int hashCode() {
return Objects.hash(firstName, middleName, lastName);
}
}
7. Table: key methods of the Objects class
| Method | Purpose | Usage example |
|---|---|---|
|
Null-safe comparison of two objects | |
|
Concise hash-code computation over multiple fields | |
|
Comparator-based comparison, null-safe | |
|
Null check, throws NullPointerException | |
|
Check for null/non-null (handy in the Stream API) | |
8. Common mistakes when using the Objects class
Mistake #1: forgot to use Objects.equals for nullable fields. If you compare fields directly via equals, you can hit a NullPointerException. Use Objects.equals(middleName, other.middleName).
Mistake #2: not all fields are accounted for in hashCode. Fields that participate in equals must also participate in hashCode, otherwise HashSet/HashMap behaviour becomes unpredictable.
Mistake #3: a bug in a hand-written hashCode. It’s not trivial to pick factors and handle null correctly. Objects.hash does it for you; use it unless you have strict performance requirements.
Mistake #4: not using Objects.requireNonNull where the class contract demands it. If a field must not be null, validate it in the constructor/setter—the error will surface immediately instead of “somewhere deep” in the call stack.
Mistake #5: using Objects.hash for arrays. For arrays use Arrays.hashCode, and for nested arrays use Arrays.deepHashCode; similarly, to compare contents use Arrays.equals/Arrays.deepEquals.
GO TO FULL VERSION