== vs equals() in Java
8 min readUpdated
== and equals() both answer "are these the same?", but they mean different things. Mixing them up is one of the most common Java bugs and one of the most common interview questions. Here is exactly what each one compares, with programs that show the surprises.
The short answer
- For primitives (
int,double,char,boolean),==compares the values. That is correct. - For objects,
==checks whether both variables point to the same object in memory. equals()compares content, as the class defines it. Use it forString,Integer,LocalDateand your own value classes.
== | equals() | |
|---|---|---|
| What it is | An operator | A method of Object, usually overridden |
| On primitives | Compares values | Not available |
| On objects | Same object (reference)? | Same content (as the class defines it)? |
| Default for your own class | Reference comparison | Also reference comparison, until you override it |
With null | x == null is safe | null.equals(x) throws NullPointerException |
Strings and the string pool
String literals with the same text are stored once in the string pool and shared, so two literals happen to be the same object. A string built with new, by joining at run time or read from input is a new object, and == returns false even though the text is the same.
public class StringEquality {
public static void main(String[] args) {
String a = "hello";
String b = "hello"; // same pooled literal
String c = new String("hello"); // a new object
String part = "hel";
String d = part + "lo"; // built at run time: a new object
System.out.println(a == b);
System.out.println(a == c);
System.out.println(a == d);
System.out.println(a.equals(c));
System.out.println(a.equals(d));
System.out.println(a == d.intern()); // intern() returns the pooled copy
}
}Output
true false false true true true
Code that compares strings with == can pass every test with literals and then fail with real user input. Always use equals(), or equalsIgnoreCase() when case does not matter.
The Integer cache trap
Integer objects for values from -128 to 127 are cached, so autoboxing small numbers returns the same object. Outside that range each boxing creates a new object. == therefore works for small numbers and silently fails for bigger ones.
public class IntegerCache {
public static void main(String[] args) {
Integer a = 127, b = 127;
Integer c = 128, d = 128;
System.out.println(a == b); // cached: same object
System.out.println(c == d); // not cached: different objects
System.out.println(c.equals(d)); // compares the values
int e = 128;
System.out.println(c == e); // unboxed to int: compares values
}
}Output
true false true true
Your own classes: override equals() and hashCode()
Object.equals() is just ==. For a value class such as Point or Money, override equals() to compare the fields, and always override hashCode() too, or the objects will not work as HashMap keys or HashSet elements. A record writes both for you.
import java.util.Objects;
import java.util.Set;
public class CustomEquals {
static class Money {
final long amount;
final String currency;
Money(long amount, String currency) {
this.amount = amount;
this.currency = currency;
}
@Override
public boolean equals(Object o) {
if (this == o) return true; // same object
if (!(o instanceof Money)) return false; // also handles null
Money m = (Money) o;
return amount == m.amount && currency.equals(m.currency);
}
@Override
public int hashCode() {
return Objects.hash(amount, currency);
}
}
record Point(int x, int y) {} // equals and hashCode generated
public static void main(String[] args) {
Money m1 = new Money(500, "INR");
Money m2 = new Money(500, "INR");
System.out.println(m1 == m2);
System.out.println(m1.equals(m2));
System.out.println(Set.of(new Point(1, 2)).contains(new Point(1, 2)));
}
}Output
false true true
The rules equals() must follow
- Reflexive:
x.equals(x)istrue. - Symmetric: if
x.equals(y)theny.equals(x). - Transitive: if
x.equals(y)andy.equals(z)thenx.equals(z). - Consistent: the result does not change unless the fields change.
- Null:
x.equals(null)isfalse, never an exception. - Equal objects must have the same
hashCode().
Comparing safely
- Put the known non-null value first:
"admin".equals(role)never throws. - Or use
Objects.equals(a, b), which handlesnullon either side. - Compare enums with
==: each constant exists exactly once. - Compare
doublevalues with a tolerance (Math.abs(a - b) < 1e-9), not==, because of rounding. - Compare
BigDecimalwithcompareTo():new BigDecimal("1.0").equals(new BigDecimal("1.00"))isfalse.
Frequently asked questions
Can I use == to compare strings in Java?
No. It compares references, so it works only by accident for pooled literals. Use equals().
Why does == work for Integer 127 but not 128?
Integer.valueOf caches the values -128 to 127, so boxing those returns the same object. 128 creates a new object each time. Use equals() or compare int values.
Do I need to override hashCode() if I override equals()?
Yes. Equal objects must have equal hash codes, or hash-based collections such as HashMap and HashSet will not find them.
Learn it step by step
Practise this, step by step
Short lessons with quizzes, practice and interview questions, from your first program to job-ready.