skip to content

equals() vs == for Strings

== compares references and equals compares content; pooling makes == accidentally work for literals, which is why the bug hides so well. Nearly every Java interview contains some form of this question.

part ofJavaoverview, primer and where to startread it →
on this pageshow

questions

4

In Java, what is the difference between using == and equals() to compare two String objects?

level: juniorimportance: must knowfreq 90%

answer

  1. == compares references (identity)
  2. equals() compares characters (content)
  3. String pool makes == look right for literals
  4. Use equals() for text, == for primitives
  5. Objects.equals() for null-safety

basics

~10 s

== checks whether two String variables point to the same object in memory. equals() checks whether the two strings contain the same characters. To compare text, always use equals().

solid answer

~40 s

String is an object, so a String variable holds a reference (a memory address), not the characters themselves. The == operator compares those references: it is true only when both variables point to the exact same object. equals(), as overridden in String, compares content character by character, returning true when the sequences of characters are identical. Because you almost always care about the text and not object identity, you should use equals() (or equalsIgnoreCase() for case-insensitive checks). A common pitfall: == can accidentally return true for string literals because of the string pool, then fail once a string is built at runtime, producing bugs that pass in simple tests but break in production. The safe rule is: == for primitives and identity, equals() for object content, including Strings.

code

java · 12 lines
java
String a = "hi";
String b = "hi";
String c = new String("hi");

System.out.println(a == b);       // true  (both pooled literal)
System.out.println(a == c);       // false (c is a new object)
System.out.println(a.equals(c));  // true  (same characters)

// Null-safe content comparison:
String input = null;
System.out.println("hi".equals(input));          // false, no exception
System.out.println(java.util.Objects.equals(a, input)); // false, no exception

go deeper

for a junior

Knows the rule: use equals() for text, == for the same object, and can state that == compares references while equals() compares characters.

for a middle

Explains the string pool and can predict == results for literals vs new String(...) vs runtime concatenation, and uses Objects.equals/null-safe ordering.

for a senior

Connects this to the general reference-vs-value model for all objects, the equals/hashCode contract, and the interning trade-offs; writes null-safe, locale-aware comparisons by habit.

for a principal

Frames it as part of API/identity design, sets team conventions (always equals() for content, Objects.equals for nullable), and reasons about pooling/interning memory implications at scale and in security-sensitive comparisons.

## First, what a String actually is In Java there are two kinds of types. **Primitives** (`int`, `boolean`, `char`, `double`, etc.) hold a raw value directly in the variable. **Reference types** (everything else, including `String`) hold a *reference* — think of it as the address of an object that lives elsewhere in memory (on the heap). The variable is a label pointing at the object; it is not the object itself. So when you write `String a = "hi";`, the variable `a` does not contain the letters h and i. It contains a pointer to a `String` object that contains those letters. ## What `==` does The `==` operator always compares the *contents of the variables*. For primitives that is the value itself (`5 == 5` is true). For reference types, the content of the variable is the reference, so `==` asks: **"do these two variables point to the exact same object?"** This is called *reference equality* or *identity*. It does NOT look inside the objects. ## What `equals()` does `equals()` is a method defined on `Object` (the root class of everything). The default `Object.equals()` just does `this == other` — identity again. But `String` *overrides* `equals()` to compare content: it checks that the other object is also a String, has the same length, and has the same character at every position. So `String.equals()` returns true exactly when the two strings represent the same text. This is *content/value equality*. ```java String a = new String("hi"); String b = new String("hi"); System.out.println(a == b); // false — two different objects System.out.println(a.equals(b)); // true — same characters ``` ## Why beginners get confused: the string pool Java keeps a special cache called the **string pool** (string intern pool). When you write a *string literal* like `"hi"` in your source code, the compiler/runtime reuses one shared object for all identical literals. So: ```java String a = "hi"; String b = "hi"; System.out.println(a == b); // true — both refer to the SAME pooled object ``` Here `==` returns `true`, which fools people into thinking `==` compares text. It does not — it just happens that both literals point to the one pooled instance. The moment a string is produced at runtime (via `new String(...)`, concatenation of variables, `StringBuilder`, reading input, substring, etc.) it is a *different* object, and `==` becomes `false` even though the text is identical: ```java String a = "hi"; String b = new String("hi"); System.out.println(a == b); // false — b is a fresh object System.out.println(a.equals(b)); // true ``` This is the classic production bug: code compares strings with `==`, works during quick literal-based testing, then fails on real runtime-built input. ## The rule to remember - Comparing **text** → use `equals()` (or `equalsIgnoreCase()` to ignore case). - Comparing **identity** ("is it literally the same object?") → use `==` (rare for strings). - For primitives → use `==` (there is no `equals` for primitives). ## Null-safety bonus Calling `equals()` on a possibly-null reference throws `NullPointerException`. To be safe, either call it on a known-non-null value (`"yes".equals(input)`) or use `java.util.Objects.equals(a, b)`, which handles nulls. `==` never throws, but it does the wrong comparison for text.

  • Why does == sometimes return true for two strings with the same text?
    Because string literals are interned in the string pool, so identical literals point to one shared object. == is true purely by coincidence of identity, not because it compares characters.
  • How do you compare two strings ignoring case?
    Use equalsIgnoreCase(), e.g. a.equalsIgnoreCase(b). Avoid lowercasing both with toLowerCase() in locale-sensitive code unless you pass a Locale, since some locales (e.g. Turkish) transform letters unexpectedly.

Two people can have identical printed copies of the same letter (equal content) without it being the same physical sheet of paper (same reference). == asks 'same sheet of paper?'; equals() asks 'same words?'.

saying these in an interview costs you the question

  • Claiming == compares string content
  • Saying equals() compares references
  • Believing == always works because it 'worked in my test' (only true for literals)
  • Calling equals() on a possibly-null variable without guarding against NPE

context

open as a page

How does String.compareTo() work, and how does it differ from equals() for ordering and sorting strings?

level: middleimportance: should knowfreq 60%

basics

~20 s

equals() answers a yes/no question: are two strings the same text? compareTo() answers an ordering question: which comes first alphabetically. It returns a negative number, zero, or a positive number, and is what sorting uses to put strings in order.

open as a page

What is the Java String pool (intern pool), and how does it affect == comparisons between strings?

level: middleimportance: should knowfreq 70%

basics

~20 s

The string pool is a cache of String objects that Java reuses for identical string literals. Because identical literals share one pooled object, == returns true for them. Strings built at runtime are not pooled, so == returns false for them.

open as a page

When you use a String as a key in a HashMap, what role do equals() and hashCode() play, and why would using == instead break lookups?

level: seniorimportance: should knowfreq 55%

basics

~20 s

HashMap finds a key by its hashCode() to pick a bucket, then uses equals() to match within that bucket. String overrides both consistently, so lookups work by content. If maps used == they would fail whenever a key was rebuilt at runtime instead of being the same object.

open as a page