keepcalculator

06 Numbers and Powers

Java == vs equals(): References, Values, and Strings

Card No. 06·28947 · Updated · Standard formula, shown below

In Java, == compares primitive values directly, but for objects it checks whether both references point to the very same object; equals() checks equality as defined by the object’s class. Strings fool beginners because Java may reuse identical string objects, making == appear to compare text correctly in some cases even though it is not a reliable content comparison.

What Each Operator Checks

The meaning of == depends on the operands. If both operands are primitive types such as int, char, or boolean, it tests whether their values are equal. For example:

int a = 7;
int b = 7;
System.out.println(a == b); // true

When both operands are reference types, == tests reference identity: do they refer to the same object? It does not inspect the object’s fields or text. The equals() method is an ordinary method inherited from Object, and a class can override it to define what equality means for its instances.

String first = new String("tea");
String second = new String("tea");
System.out.println(first == second);      // false
System.out.println(first.equals(second)); // true

These expressions are not competing spellings of one operation. One asks whether two references identify the same object; the other asks whether the objects should count as equal according to their class’s implementation.

Why Strings Seem to Break the Rule

Java maintains a string pool for string literals. When the same literal is used more than once, those literal expressions can refer to a shared, interned string object. That makes this code print true:

String one = "coffee";
String two = "coffee";
System.out.println(one == two); // often, and by the language's literal rules, true

The result is true because the references identify the same pooled object, not because == compared the characters. Contrast that with strings built separately:

String one = "cof" + "fee";
String two = new String("coffee");
System.out.println(one.equals(two)); // true
System.out.println(one == two);      // false

A compiler may combine constant expressions such as "cof" + "fee" into a single literal. The explicit constructor creates a distinct object. Code that relies on whether a particular expression is pooled is brittle and obscures intent. Use equals() when the question is whether two strings contain the same sequence of characters.

How to Compare Strings Safely

For ordinary, case-sensitive text equality, call equals():

if (name.equals("Mira")) {
    // exact character match
}

There is one common complication: if name is null, calling a method on it throws NullPointerException. If the expected value is a known non-null literal, put that literal first:

if ("Mira".equals(name)) {
    // safe even if name is null
}

Alternatively, check for null explicitly or use Objects.equals(a, b), which returns true when both references are null, false when just one is null, and otherwise delegates to equality. For case-insensitive comparison, use equalsIgnoreCase() when that is the intended rule; it is not a substitute for locale-aware text processing in every language-sensitive task.

  • Use == for primitive values and when you intentionally need object identity.
  • Use String.equals() for exact string content.
  • Use equalsIgnoreCase() only when ignoring case is the intended comparison.
  • Handle possible null references before invoking an instance method.

Custom Objects and the Equality Contract

For a class that does not override equals(), the implementation inherited from Object treats distinct instances as unequal, much like identity comparison. A class can override the method to compare meaningful state—for example, two Point objects might be equal when their coordinates match.

Any implementation of equals() should follow its contract: it must be reflexive, symmetric, transitive, consistent while relevant state is unchanged, and return false when compared with null. When equals() is overridden, hashCode() must also be overridden so equal objects produce the same hash code. This matters in hash-based collections such as HashSet and HashMap: breaking the relationship can cause lookups or membership checks to behave unexpectedly.

In modern Java, records provide generated equality based on their record components. For other classes, IDEs or libraries can generate implementations, but the chosen fields and mutability still matter. If fields used for equality change after an object is placed in a hash-based collection, the collection may no longer find it where expected.

Identity, Equality, and Choosing the Right Test

Identity and equality answer different questions. Identity is useful when the program cares whether two variables refer to one particular object—for example, comparing a sentinel object or checking whether a reference is exactly a designated instance. Equality is useful when separate objects with the same meaningful data should be treated alike.

For arrays, note a related trap: arrays do not compare their contents with the usual inherited equals(); two separate arrays with the same elements are not equal by that method. Use Arrays.equals() for one-dimensional array content or the suitable deep-equality method for nested arrays. For collections, their equality behavior is generally based on their documented contents and comparison rules, but the element type’s own equality implementation still matters.

Also distinguish equality from ordering. A class may implement Comparable or use a Comparator to define sorting order. A comparison returning zero is often intended to align with equals(), but Java does not require that in every case; the relevant class documentation should say whether it does.

Common Mistakes

  • Using == for string text: a test may pass for pooled literals and fail for separately created strings. Replace it with equals().
  • Calling equals() on a nullable value: check for null, use a known non-null receiver, or use Objects.equals().
  • Assuming every class has useful value equality: inspect or implement equals(); the inherited default does not compare arbitrary fields.
  • Overriding equals() but not hashCode(): equal objects may then fail to work correctly in hash-based collections.
  • Confusing case or normalization with equality: ordinary string equality compares character sequences exactly; decide explicitly whether case or Unicode normalization should affect the application’s rule.

Frequently Asked Questions

Does == compare string contents in Java?
No. For strings, it checks whether both references point to the same object. Use equals() to compare character sequences.

Why does "java" == "java" return true?
Identical string literals are interned and can refer to the same pooled object. That is an identity result, not a content comparison by ==.

Is equals() always safe?
No. Calling it on a null reference throws NullPointerException. Use a null check, a non-null expected value as the receiver, or Objects.equals().

What is the difference between equals() and equalsIgnoreCase()?
equals() requires the same case and character sequence. equalsIgnoreCase() compares strings without distinguishing letter case, subject to its Java-defined behavior.

Should I use == or equals() for numbers?
For primitive numbers, == compares numeric values. For wrapper objects such as Integer, == compares references; use equals() for object value equality, while taking care with nulls.

Related

Method: the standard formula for this calculation, rounded as each result states. See how the calculators work or report an error.

More cards in this drawer

The whole Numbers and Powers drawer →