なぜ「文字列比較」はつまずきポイントなのか
Java 初心者がほぼ確実に一度はハマるのが、文字列比較でのミスです。
見た目は同じ "abc" なのに、なぜか比較結果が false になる、if が通らない、バグる。
原因のほとんどは、
==とequalsの違いをちゃんと分かっていない- null が来たときの扱いを考えていない
この2つです。
ここをきちんと押さえておくと、文字列比較で悩むことがほぼなくなります。
「どう書くのが“正解パターン”なのか」を、例を交えながら整理していきます。
大前提:== と equals はまったく別物
== は「同じインスタンスか?」しか見ていない
まず、これをはっきり区別しましょう。
==
参照型(String など)の場合は、「同じオブジェクト(同じアドレス)を指しているか」を比較。
equals
「中身(値)が等しいか」を比較するためのメソッド。
例を見てください。
String a = new String("abc");
String b = new String("abc");
System.out.println(a == b); // false
System.out.println(a.equals(b)); // true
Java見た目はどちらも "abc" ですが、
aとbは別々にnewされたインスタンス- だから
==は false - でも「文字列としての中身」は同じなので
equalsは true
文字列の内容を比較したいときは、必ず equals を使う。
ここが文字列比較の大前提です。
たまたま == が true になるケースがあるからややこしい
次のコードを見てください。
String a = "abc";
String b = "abc";
System.out.println(a == b); // true に見える
System.out.println(a.equals(b)); // もちろん true
Java== が true になっています。
これは Java の「文字列リテラルのインターン(プール)」の仕組みのせいで、
同じ "abc" リテラルは同じインスタンスを共有しているからです。
この挙動のせいで、初心者は「== でもいいのかな?」と勘違いしがちです。
しかし、次のようなケースで簡単に裏切られます。
String a = "abc";
String b = new String("abc");
System.out.println(a == b); // false
System.out.println(a.equals(b)); // true
Javaその時々で結果が変わるようなものに頼るのは危険です。
「値として同じか見たいなら equals」、これは絶対ルールとして覚えてください。
正解パターン1:基本の equals 比較
一番オーソドックスな書き方
ある文字列変数 input が "yes" と等しいか確認したいときの素直な書き方:
if (input != null && input.equals("yes")) {
// "yes" と入力されたときの処理
}
Javaここで大事なのは、
input.equals("yes")だけ書くと、inputが null のときにNullPointerException- それを避けるために、先に
input != nullをチェックしている
という点です。
null の可能性がある変数には、いきなり equals を呼ばない。
必ず「null チェック → equals」という順番を守ります。
正解パターン2:null 安全な "リテラル側から equals" テクニック(重要)
"xxx".equals(variable) の形にする
もっと楽で安全な書き方があります。
それが 「null にならない側から equals を呼ぶ」 というテクニックです。
if ("yes".equals(input)) {
// "yes" と入力されたときの処理
}
Javaこう書けば、
"yes"は絶対に null にならないinputが null でも"yes".equals(null)は false を返すだけ- つまり、NullPointerException が絶対に起きない
input が null の可能性がある場面では、ほぼいつでもこのスタイルが使えます。
他の例も見てみます。
String command = ...;
if ("START".equals(command)) {
// 開始
} else if ("STOP".equals(command)) {
// 停止
}
Javaどの比較も安全です。
「リテラル(固定文字列)を左側に置く」のを癖にしてしまうと、null で困ることがガクッと減ります。
大文字・小文字を無視したいとき:equalsIgnoreCase
ユーザー入力などでよくある要件
「yes, YES, Yes どれでも OK にしたい」ということはよくあります。
そのときに、こんな書き方をしがちです。
if ("yes".equals(input) || "YES".equals(input)) {
...
}
Javaこれは冗長ですし、漏れも出やすいです。
こういうときは equalsIgnoreCase を使います。
if ("yes".equalsIgnoreCase(input)) {
// 大文字小文字を無視して "yes" と等しい場合
}
Javaポイントは、これも "yes" 側から呼べば null 安全になることです。
"yes".equalsIgnoreCase(input) はinput が "YES" でも "Yes" でも "yEs" でも trueinput が null のときは false(例外は出ない)
になります。
「含む」「前方一致」「後方一致」などの比較
部分一致:contains, startsWith, endsWith
文字列比較は、「完全一致」だけとは限りません。
よくあるのは、以下のようなケースです。
- 「この文字列を含んでいるか」→
contains - 「この文字列で始まっているか」→
startsWith - 「この文字列で終わっているか」→
endsWith
例を出します。
String text = "Java入門コース";
boolean b1 = text.contains("入門"); // true
boolean b2 = text.startsWith("Java"); // true
boolean b3 = text.endsWith("コース"); // true
Javaここでも、「null の可能性」がある変数側に直接呼び出さないように注意します。
危険な書き方:
String text = null;
if (text.contains("Java")) { ... } // NullPointerException
Java安全な書き方の例:
String text = ...;
if (text != null && text.contains("Java")) {
...
}
Javaもしくは、「text が null ならそもそもチェックしない」という設計にしておく、などです。
compareTo と == / equals の違い(少しステップアップ)
compareTo は「順序比較」に使う
String には compareTo というメソッドもあります。
int result = s1.compareTo(s2);
Javaこれは、
- 0 → 「s1 と s2 は同じ」
- 0 より小さい → 「s1 は s2 より“辞書順で前”」
- 0 より大きい → 「s1 は s2 より“辞書順で後”」
という意味を持ちます。
例えば:
System.out.println("abc".compareTo("abc")); // 0
System.out.println("abc".compareTo("abd")); // 負の値(abcの方が前)
System.out.println("abd".compareTo("abc")); // 正の値(abdの方が後)
Java主に
- ソート(並び替え)のとき
- 「どちらが大きい・小さいか」を比べたいとき
に使います。
ただし、「等しいかどうか」だけ見たいなら、素直に equals を使うほうが読みやすいです。
if (s1.equals(s2)) { ... } // 中身が同じかどうか
if (s1.compareTo(s2) == 0) { ... } // これでもいいが、少しまわりくどい
Javaよくある「間違った比較」とその直し方
パターン1:== を使ってしまう
間違い:
String status = getStatus(); // どこかから取得
if (status == "OK") {
// ついこう書いてしまう
}
Java直し方(null の可能性も考える):
if ("OK".equals(status)) {
...
}
Javaパターン2:null を考えずに equals を呼ぶ
間違い:
String s = null;
if (s.equals("abc")) { // NullPointerException
...
}
Java直し方(リテラル側から呼ぶ):
if ("abc".equals(s)) {
...
}
Javaもしくは明示的に null チェック:
if (s != null && s.equals("abc")) {
...
}
Javaただし、癖づけやすさを考えると、普段は "abc".equals(s) スタイルがおすすめです。
まとめ:「文字列比較の正解」をあなたの頭の中に固定する
初心者として、ここだけ押さえておけば大きくハマらない、というポイントを整理します。
- 内容の比較は必ず
equals。==は使わない。 - null の可能性がある変数には、直接
変数.equals(...)を呼ばない。 "固定文字列".equals(変数)という書き方を癖にすると、null でコケない。- 大文字小文字を無視したいときは
equalsIgnoreCase。 - 部分一致なら
contains、前方一致ならstartsWith、後方一致ならendsWith。 - 順序比較やソートが必要なときだけ
compareToを意識する。
