Java String Comparison

In the last lesson you learned about Java String methods. Now for the job that causes more bugs than any other: checking if two Strings are the same. You type if (a == b), the words look identical, and the check still says false.

🤔 Why comparing text is tricky

A String is two things at once, so “same” has two meanings:

  • The text you see, like “yes” — same characters is equality.
  • An object sitting in memory — same object is identity.

Almost always you care about the characters (right password? names match?), not where the text lives. Java has a clear tool for that.

🟰 What == really checks

The == operator does not compare text. It compares references — it asks “do these two variables point at the exact same object?”. That is identity, not characters.

This example shows == giving the wrong answer for text that clearly matches.

String a = "Hello";
String b = new String("Hello");
System.out.println(a == b); // are they the same object?

Here is why == says no:

  • Both hold “Hello”, so they look identical.
  • But new String("Hello") builds a brand new object, separate from a.
  • == asks “same object?” — and they are not.

Output

false

This is the number one String bug for beginners. Remember: == looks at the box, not at what is written inside it.

✅ Use .equals() to compare the characters

To compare the actual text, use .equals(). It checks both Strings character by character and returns true only when every character matches, capital letters included.

This example fixes the bug from above by switching to .equals().

String a = "Hello";
String b = new String("Hello");
System.out.println(a.equals(b)); // do the characters match?
System.out.println(a.equals("Hello"));
System.out.println(a.equals("hello")); // case is different

Reading the results:

  • a.equals(b) is true — both hold H-e-l-l-o, different objects or not.
  • a.equals("Hello") is true for the same reason.
  • a.equals("hello") is false — capital H and small h do not match. equals is strict about case.

Output

true
true
false

The rule you will use forever: to compare what two Strings say, use .equals(). To check if they are the same object (rare), use ==.

🔠 Ignore the case with .equalsIgnoreCase()

People type “YES”, “yes”, “Yes” — all meaning the same thing. To treat them as equal, use .equalsIgnoreCase(). It compares the text but treats capital and small letters as the same.

This example checks an answer typed in three different ways.

String answer = "Yes";
System.out.println(answer.equals("yes")); // strict on case
System.out.println(answer.equalsIgnoreCase("yes")); // case ignored
System.out.println(answer.equalsIgnoreCase("YES"));
System.out.println(answer.equalsIgnoreCase("no")); // different word

What each line gives back:

  • equals("yes") is false — “Yes” and “yes” differ in the first letter’s case.
  • equalsIgnoreCase("yes") is true — ignore case and they match.
  • equalsIgnoreCase("YES") is true for the same reason.
  • equalsIgnoreCase("no") is false — the words really are different.

Output

false
true
true
false

Use equalsIgnoreCase whenever case should not matter: a yes/no answer, an email address, a typed command.

🔢 Order with .compareTo()

When you need the order (for sorting names), use .compareTo(). It compares two Strings in dictionary order and hands back a number:

  • Negative — the first String comes before the other.
  • Zero — the two are exactly equal.
  • Positive — the first String comes after the other.

This example compares a few words.

System.out.println("apple".compareTo("banana")); // apple comes first
System.out.println("banana".compareTo("apple")); // banana comes later
System.out.println("apple".compareTo("apple")); // equal

Reading the results:

  • "apple".compareTo("banana") is negative — “apple” comes before “banana”.
  • "banana".compareTo("apple") is positive — “banana” comes after.
  • "apple".compareTo("apple") is 0 — equal.

Output

-1
1
0

Read the sign, not the size. Negative, zero, or positive is all you need — and it is exactly what Java’s sorting tools use to order a list of words.

compareTo is case-sensitive too

Capital letters count as “smaller” than small letters here, because of how characters are numbered. So "Banana".compareTo("apple") is negative, even though B comes after a in your head. When case should not matter, use compareToIgnoreCase instead.

🔡 Order without case using .compareToIgnoreCase()

When you want dictionary order but capital letters keep messing it up, use .compareToIgnoreCase(). It works just like compareTo but treats “A” and “a” as the same.

This example shows the difference clearly.

System.out.println("Banana".compareTo("apple")); // case matters
System.out.println("Banana".compareToIgnoreCase("apple")); // case ignored

What the two lines mean:

  • First is negative — capital B is numbered lower than small a, so it wrongly claims “Banana” comes first.
  • Second is positive — with case ignored, “banana” correctly comes after “apple”.

Output

-31
1

So when you sort a list people will read, compareToIgnoreCase usually gives the order they expect.

🧱 Comparing with .contentEquals()

One more equality tool: .contentEquals(). It checks the characters just like equals, but it also accepts a StringBuilder, not only a String. You will meet StringBuilder next lesson; for now, it is another way to hold text.

This example compares a String against both a String and a StringBuilder.

String name = "Hello";
StringBuilder sb = new StringBuilder("Hello");
System.out.println(name.contentEquals("Hello")); // compare to a String
System.out.println(name.contentEquals(sb)); // compare to a StringBuilder

Why both are true:

  • Both compare the same characters, H-e-l-l-o.
  • Plain equals would return false on the second line — a String and a StringBuilder are different types.
  • contentEquals looks past the type and just compares the characters.

Output

true
true

You will not reach for it daily. But when you have built text in a StringBuilder and want to check what it says, this does it cleanly.

🏊 The string pool, and why "a" == "a" can be true

Here is the part that fools people into thinking == works for text. Sometimes it seems to. Look at this.

String a = "Hello";
String b = "Hello";
System.out.println(a == b); // true?!

This prints true, which seems to break the rule. Here is why:

  • Java keeps a storage area called the string pool.
  • For a plain literal like "Hello", Java checks the pool first and reuses the existing object.
  • So a and b point at the one shared “Hello” — same object, so == is true. True by luck, not because == compares text.

Output

true

Now watch new String. The keyword new forces a fresh object, skipping the pool on purpose.

String a = "Hello";
String b = new String("Hello");
System.out.println(a == b); // different objects
System.out.println(a.equals(b)); // same characters

Now a is the pooled “Hello” and b is a separate object — == sees two objects (false), equals sees the same characters (true).

Output

false
true

This is the trap: == passes for pooled literals, then fails the moment a String comes from new, from user input, or from joining text at runtime. Never use == for text, even when it seems to work.

⌨️ Comparing user-entered strings

Text from a user almost never lives in the pool. Java builds a fresh object for whatever someone types, so == against that input is false even when the words match perfectly.

This example reads a word and compares it both ways.

import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.print("Type yes to continue: ");
String input = scanner.nextLine();
System.out.println(input == "yes"); // unreliable for input
System.out.println(input.equals("yes")); // correct
}
}

Even if the person types exactly “yes”:

  • The == line usually prints false — their input is a different object from the literal “yes”.
  • The equals line prints true — it compares the actual characters.

So for anything a user types, reads from a file, or joins at runtime, always use equals.

Output

Type yes to continue: yes
false
true

🛡️ Null-safe comparison: put the literal first

One habit saves you from a crash. If a String variable is null, calling a method on it throws a NullPointerException — there is no object there to ask. So input.equals("yes") crashes when input is null.

The fix: put the known literal on the left and the variable on the right.

String input = null;
System.out.println("yes".equals(input)); // safe, no crash

Why this is safe:

  • The literal “yes” is never null, so calling equals on it always works.
  • equals returns false when the other side is null.
  • So you get a clean false instead of a crash. (This flip is sometimes called “Yoda” style.)

Output

false

Make it a habit: whenever you compare a variable that might be null against a fixed word, put the fixed word first.

⚠️ Common Mistakes

These slip-ups cause most String comparison bugs.

Using == to compare text. It checks identity, not characters, so it fails the moment the String is not pooled.

String input = new String("yes");
if (input == "yes") { } // ❌ false even though the text matches
if (input.equals("yes")) { } // ✅ compares the actual characters

Calling equals on a String that might be null. This throws a NullPointerException at runtime.

String input = null;
if (input.equals("yes")) { } // ❌ NullPointerException, input is null
if ("yes".equals(input)) { } // ✅ literal first, safely returns false

Using compareTo when you meant equals. compareTo returns a number, and 0 means equal. People test it like true/false and get it backwards.

String a = "yes";
if (a.compareTo("yes") == 1) { } // ❌ wrong: equal is 0, not 1
if (a.equals("yes")) { } // ✅ use equals for a yes/no check

✅ Best Practices

Habits that keep String comparison correct and crash-free.

  • Use equals for text, never ==. == checks objects, not characters, and it will let you down.
  • Use equalsIgnoreCase when case should not matter. Yes/no answers, emails, and commands are good examples.
  • Put the literal first to stay null-safe. Write "yes".equals(input), not input.equals("yes").
  • Reach for compareTo only for ordering. Negative, zero, positive tells you the dictionary order, used for sorting.
  • Read the sign of compareTo, not the size. You care whether it is negative, zero, or positive, not the exact number.
  • Remember the pool is not your friend here. == passing for "a" == "a" is luck, not a rule. Do not depend on it.

🧩 What You’ve Learned

That clears up the trickiest part of Strings.

  • == compares references (the object), not the text, so it is the number one String bug.
  • .equals() compares the actual characters, and .equalsIgnoreCase() does it without caring about case.
  • .compareTo() returns negative, zero, or positive for dictionary order; .compareToIgnoreCase() ignores case.
  • .contentEquals() compares characters and also accepts a StringBuilder.
  • ✅ The string pool is why "a" == "a" can be true, but new String("a") == "a" is false.
  • ✅ Always compare user input with equals, and put the literal first for null-safe checks.

Check Your Knowledge

Test what you learned. Pick an answer for each question, then click Check.

  1. 1

    What does == actually compare for two Strings?

    Why: == compares references, meaning whether both variables point at the same object. It does not compare the text.

  2. 2

    Which method compares the actual characters and ignores capital letters?

    Why: equalsIgnoreCase compares the text but treats capital and small letters as the same.

  3. 3

    What does "apple".compareTo("banana") return?

    Why: It returns a negative number because 'apple' comes before 'banana' in dictionary order.

  4. 4

    Why write "yes".equals(input) instead of input.equals("yes")?

    Why: The literal 'yes' is never null, so calling equals on it is always safe, even when input is null.

🚀 What’s Next?

Comparing text is solved. But building text by joining over and over is slow — each join makes a new object. For heavy text building, Java has a faster tool that changes in place. Let’s learn it next.

Java StringBuilder

Share & Connect