Java | Java 標準ライブラリ:文字列比較の正解

Java Java
スポンサーリンク

なぜ「文字列比較」はつまずきポイントなのか

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" ですが、

  • ab は別々に 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" でも true
input が 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 を意識する。

タイトルとURLをコピーしました