Javaの比較演算子を完全解説|==・!=・<・>とequals()の違いまで

目次

1. この記事で分かること

Javaの「比較演算子」は、数値や文字(char)などの“プリミティブ型”を比べるときに使う基本文法です。ところが実務でつまずきやすいのは、StringInteger などの参照型(オブジェクト)を比べようとして == を使ってしまうケースです。

このセクションでは、まず最短で迷いが消えるように、結論だけ先に整理します。

1.1 比較演算子の基本は「大小」と「一致」の2種類

Javaの比較演算子は、大きく分けて次の2系統です。

  • 大小を比較する< / <= / > / >=
  • 等しい・等しくないを比較する== / !=

プリミティブ型(int / long / double / char / boolean など)に対しては、これらを素直に使えばOKです。

int a = 10, b = 20;
System.out.println(a < b);   // true
System.out.println(a == b);  // false

1.2 重要:== は「値」ではなく「同じモノか」を見る場合がある

ここが最大の落とし穴です。

  • プリミティブ型に対する ==値が同じか
  • 参照型(オブジェクト)に対する ==同じインスタンス(同じ参照)か

たとえば String== で比較すると、文字列の内容が同じでも false になることがあります。

String s1 = new String("Java");
String s2 = new String("Java");

System.out.println(s1 == s2);      // false(同じインスタンスではない)
System.out.println(s1.equals(s2)); // true(内容は同じ)

つまり、「文字列が同じか?」を判定したいなら equals() を使うのが基本です。

1.3 迷ったらこれ:使い分けの早見表

初心者のうちは、次の基準だけでほぼ困りません。

  • 数値・char・boolean(プリミティブ型)
    == / != / < / > / <= / >= を使う
  • 文字列(String)やオブジェクトの“内容”
    equals()(または Objects.equals())を使う
  • 文字列やオブジェクトの“順序”(大小・並び)
    compareTo()(または Comparator)を使う

ポイント:
== は「同じ値か?」ではなく「同じモノか?」の意味になりうる、と覚えるだけで事故が激減します。

1.4 この記事のゴール(読み終わるとできること)

この記事を最後まで読むと、次のことがスムーズに判断できるようになります。

  • ==!=安全に使える場面/危険な場面が分かる
  • String 比較で == を使ってしまうミスを防げる
  • Integer などのラッパークラス比較で、結果がブレる原因を理解できる
  • 「大小比較がしたいのに比較演算子が使えない」場面で、compareTo() に自然に切り替えられる

2. Javaの比較演算子とは(一覧)

このセクションでは、Javaで使える比較演算子を体系的に整理します。
初心者の方が「どの演算子を、どんな場面で使えばよいか」を迷わないことが目的です。

2.1 比較演算子は「真(true)か偽(false)」を返す

Javaの比較演算子は、計算結果として必ず boolean 型(true / false)を返すという特徴があります。
そのため、if 文や while 文などの条件式で頻繁に使われます。

int x = 5;
int y = 10;

boolean result = x < y;  // true

比較演算子自体は値を変更せず、「条件が成り立つかどうか」だけを判定します。

2.2 大小を比較する演算子:< <= > >=

数値や文字(char)の大小関係を調べるときに使います。

演算子意味
<左が右より小さい
<=左が右以下
>左が右より大きい
>=左が右以上

数値の比較例

int a = 10;
int b = 20;

System.out.println(a < b);   // true
System.out.println(a >= b);  // false

char(文字)の比較例

char は内部的に数値(Unicode)として扱われるため、大小比較が可能です。

char c1 = 'A';
char c2 = 'B';

System.out.println(c1 < c2); // true

String にはこれらの演算子は使えません(後述します)。

2.3 等しい・等しくないを比較する演算子:== !=

次に、一致・不一致を判定する演算子です。

演算子意味
==等しい
!=等しくない

プリミティブ型での使用例

int x = 5;
int y = 5;

System.out.println(x == y); // true
System.out.println(x != y); // false

この場合、値そのものが比較されるため直感どおりの結果になります。

2.4 boolean同士の比較もできる

boolean 型同士も == / != で比較できます。

boolean f1 = true;
boolean f2 = false;

System.out.println(f1 == f2); // false
System.out.println(f1 != f2); // true

ただし、if (flag) のように直接条件に使う方が読みやすいケースが多いため、実務では多用されません。

2.5 比較演算子が使える型・使えない型

ここで一度、使える対象を整理しておきます。

比較演算子が素直に使える型

  • int
  • long
  • double
  • float
  • char
  • boolean== / != のみ)

比較演算子がそのままでは使えない型

  • String
  • Integer / Long などのラッパークラス
  • 独自クラス(オブジェクト)

これらは「参照型」であり、== の意味が変わるため注意が必要です。

2.6 この時点で覚えておくべき重要ポイント

初心者の段階では、次の2点だけ押さえておけば十分です。

  • 比較演算子は条件判定専用で、結果は必ず true / false
  • 数値・char 以外(特に String)に == を使うと危険

3. プリミティブ型は比較演算子でOK

このセクションでは、比較演算子を安心して使えるケースとして、プリミティブ型の比較を整理します。
ここを正しく理解しておくと、「どこからが危険ゾーンなのか」がはっきりします。

3.1 プリミティブ型とは何か(おさらい)

Javaのプリミティブ型は、値そのものを直接保持する型です。
代表的なものは次のとおりです。

  • 数値型:int / long / double / float / short / byte
  • 文字型:char
  • 真偽値:boolean

これらは参照ではなく値を比較するため、==< などの比較演算子をそのまま使えます。

3.2 数値型の比較(int / long / double など)

int / long の例

int a = 100;
int b = 100;

System.out.println(a == b); // true
System.out.println(a < b);  // false

直感どおり、「数値が同じか」「どちらが大きいか」を判定できます。

型が違う場合の比較

int x = 10;
long y = 10L;

System.out.println(x == y); // true

数値型同士は、暗黙的に型変換されたうえで比較されます。

3.3 char の比較は「文字」ではなく「コード値」

char は見た目は文字ですが、内部的には数値(Unicode)です。

char c1 = 'A';
char c2 = 'a';

System.out.println(c1 < c2); // true

この結果は、「A」と「a」の文字コードの大小で決まります。
アルファベット順・日本語順を意図して比較したい場合は、String として扱う必要があります。

3.4 boolean の比較と実務での考え方

boolean 型も == / != で比較できます。

boolean f1 = true;
boolean f2 = false;

System.out.println(f1 == f2); // false

ただし、実務では次の書き方が好まれます。

if (isActive) {
    // 処理
}
if (!isActive) {
    // 処理
}

== true== false は冗長になりがちなので、読みやすさを意識しましょう。

3.5 浮動小数点数(double / float)の比較は注意

ここは初心者が必ず一度はハマるポイントです。

double d1 = 0.1 + 0.2;
double d2 = 0.3;

System.out.println(d1 == d2); // false になることがある

これは、浮動小数点数が誤差を含む形式で表現されるためです。

安全な比較方法(許容誤差を使う)

double eps = 0.000001;

if (Math.abs(d1 - d2) < eps) {
    // ほぼ等しいとみなす
}

金額計算など厳密さが必要な場合は、BigDecimal の利用を検討します。

3.6 このセクションのまとめ

  • プリミティブ型は 比較演算子をそのまま使ってOK
  • char の比較は文字コード順になる
  • double / float== は誤差問題に注意
  • ここまでが「安全地帯」

4. 参照型で「==」を使うとハマる理由

ここからが、多くの初心者〜中級者が一度はつまずくポイントです。
StringInteger などの参照型(オブジェクト)に対して比較演算子を使うと、プリミティブ型とは意味が変わることを正しく理解する必要があります。

4.1 参照型では「==」は“同じモノか”を比較する

参照型の変数には、値そのものではなく「オブジェクトの場所(参照)」が入っています。

そのため、== を使うと次のような判定になります。

  • プリミティブ型:値が同じか
  • 参照型:同じインスタンス(同じ参照)か

Stringでの典型的な例

String s1 = new String("Java");
String s2 = new String("Java");

System.out.println(s1 == s2); // false

見た目は同じ文字列でも、new によって別々のオブジェクトが生成されているため、== は false になります。

4.2 equals() は「中身(内容)」を比較する

equals() メソッドは、オブジェクトの内容が等しいかどうかを判定するためのものです。

System.out.println(s1.equals(s2)); // true

String クラスでは equals() がオーバーライドされており、文字列の内容を比較する実装になっています。

基本ルール(超重要)

  • 文字列が同じか?equals()
  • 同じインスタンスか?==

この使い分けを覚えるだけで、比較に関するミスは大幅に減ります。

4.3 Stringリテラルだと「==」がtrueになることがある理由

初心者をさらに混乱させるのが、次のケースです。

String a = "Java";
String b = "Java";

System.out.println(a == b); // true

これは String Pool(文字列プール) という仕組みが関係しています。

  • 同じ文字列リテラルは、Java内部で共有される
  • 結果として、同じ参照を指す場合がある

しかし、これに依存したコードを書くべきではありません

String x = "Java";
String y = new String("Java");

System.out.println(x == y);      // false
System.out.println(x.equals(y)); // true

結論:
Stringの比較は常に equals() を使う
「==でも動いた」は偶然です。

4.4 nullが混ざると equals() も危険になる

次のコードは、実務で非常によくある例です。

String str = null;

if (str.equals("Java")) {
    // NullPointerException
}

strnull の場合、equals() を呼び出すと NullPointerException が発生します。

安全な書き方①:定数側から equals()

if ("Java".equals(str)) {
    // OK
}

安全な書き方②:Objects.equals()

if (Objects.equals(str, "Java")) {
    // OK(null安全)
}

特に条件分岐が多いコードでは、Objects.equals() を使うと安全性と可読性が向上します。

4.5 このセクションのまとめ

  • 参照型に対する == は「同じインスタンスか」を見る
  • 文字列やオブジェクトの内容比較は equals() を使う
  • String Pool によって == が true になる場合があり、混乱の原因になる
  • nullが来る可能性がある場合は null安全な比較方法を選ぶ

5. ラッパークラス(Integer など)の比較に注意

String の次に多い落とし穴が、IntegerLong などのラッパークラスの比較です。
一見すると数値なので問題なさそうに見えますが、参照型である点が混乱の原因になります。

5.1 ラッパークラスとは何か

ラッパークラスは、プリミティブ型をオブジェクトとして扱うためのクラスです。

プリミティブ型ラッパークラス
intInteger
longLong
doubleDouble
booleanBoolean

これらはすべて参照型です。

5.2 Integer を「==」で比較すると結果が変わる理由

次のコードを見てください。

Integer a = 100;
Integer b = 100;

System.out.println(a == b); // true

一方、次はどうでしょうか。

Integer x = 1000;
Integer y = 1000;

System.out.println(x == y); // false

同じ「数値の比較」に見えるのに、結果が違います。

原因:Integerキャッシュ

Javaでは、一定範囲のInteger値がキャッシュされる仕組みがあります(一般的に -128 ~ 127)。

  • キャッシュ範囲内 → 同じ参照を使う場合がある
  • キャッシュ範囲外 → 別インスタンスになる

つまり、== の結果が 値ではなく内部実装に依存してしまうのです。

5.3 ラッパークラスの値比較は equals() が基本

ラッパークラス同士で数値として等しいかを判定したい場合は、equals() を使います。

Integer a = 1000;
Integer b = 1000;

System.out.println(a.equals(b)); // true

これなら、値に基づいた比較が行われます。

5.4 オートボクシング/アンボクシングの罠

Javaでは、次のようなコードも書けます。

Integer a = 100;
int b = 100;

System.out.println(a == b); // true

この場合、a自動的にアンボクシングされ、int として比較されます。

一見便利ですが、

  • null が入ると NullPointerException
  • コードの意図が読み取りにくい

といった問題があるため、明示的な equals() 比較の方が安全です。

5.5 推奨される比較パターン

ラッパークラスを扱うときの基本方針は次のとおりです。

  • 値として比較したい
    a.equals(b) または Objects.equals(a, b)
  • null の可能性がある
    Objects.equals(a, b)
  • 参照が同一か確認したい特殊なケース
    ==(意図が明確な場合のみ)

5.6 このセクションのまとめ

  • ラッパークラスは参照型なので == は危険
  • Integerキャッシュにより、結果がブレることがある
  • 値比較は equals() が基本
  • null安全が必要なら Objects.equals()

6. null安全に比較する方法

実務のJavaコードでは、「比較対象が null になる可能性」を無視できません。
比較演算子や equals() を不用意に使うと、**実行時エラー(NullPointerException)**につながります。

このセクションでは、安全に比較するための定番パターンを整理します。

6.1 null と比較演算子の基本ルール

まず大前提として、次の点を押さえておきます。

  • null == nulltrue
  • null != nullfalse
  • null < 何かコンパイルエラー
  • null.equals(...)NullPointerException

つまり、null に対して メソッド呼び出しはできない、という点が重要です。

6.2 危険なパターン:変数側から equals() を呼ぶ

次のコードは非常によく見かけます。

String str = null;

if (str.equals("Java")) {
    // 処理
}

このコードは、strnull の場合に 即エラーになります。
比較自体が正しくても、呼び出し方が危険です。

6.3 安全な定番①:定数側から equals() を呼ぶ

最もシンプルで広く使われている方法です。

if ("Java".equals(str)) {
    // OK
}

この書き方のポイントは、

  • "Java" は null にならない
  • str が null でも問題なく比較できる

という点です。

文字列リテラルとの比較では、まずこの書き方を覚えると安心です。

6.4 安全な定番②:Objects.equals() を使う

Java標準ライブラリには、null安全な比較メソッドが用意されています。

import java.util.Objects;

if (Objects.equals(str, "Java")) {
    // OK
}

Objects.equals(a, b) は、内部で次のように動作します。

  • 両方 null → true
  • 片方だけ null → false
  • 両方非null → a.equals(b)

そのため、nullチェックを自分で書く必要がありません

6.5 Objects.equals() が向いているケース

次のような場面では、Objects.equals() が特に有効です。

  • 比較対象が 変数同士
  • null の可能性を 完全に排除できない
  • 条件式を簡潔に書きたい
if (Objects.equals(user.getStatus(), prevStatus)) {
    // 状態が同じ
}

6.6 nullを許容しない設計という選択肢

補足として、設計面の考え方も触れておきます。

  • 「本来 null にならない値」は
    初期化を保証する
  • null が意味を持つなら
    Optional の利用を検討する

比較処理が増えるほど、nullを前提にしたコードは複雑化します。
比較演算子の話題とあわせて、「nullを減らす設計」も意識すると、コード全体の品質が上がります。

6.7 このセクションのまとめ

  • equals() を変数側から呼ぶのは危険
  • 定数側 equals() または Objects.equals() を使う
  • null安全な比較は実務で必須の知識
  • 設計段階で null を減らすことも重要

7. 大小比較したい場合は compareTo を使う

ここまでで、比較演算子と equals() の使い分けは整理できました。
しかし、次のような疑問を持つ人も多いはずです。

文字列やオブジェクトで
「どちらが大きいか」「順番はどちらか」
を比較したい場合はどうするの?

その答えが compareTo() です。

7.1 compareTo() とは何か

compareTo() は、順序(大小関係)を比較するためのメソッドです。
多くのクラスは Comparable インターフェースを実装しており、このメソッドを持っています。

戻り値のルールは次のとおりです。

  • 負の値:左が右より小さい
  • 0:等しい
  • 正の値:左が右より大きい

7.2 String の大小比較例(辞書順)

String a = "Apple";
String b = "Banana";

int result = a.compareTo(b);

if (result < 0) {
    System.out.println("a は b より前");
}

この比較は、アルファベット順(Unicode順)で行われます。

注意
String<> は使えません。
コンパイルエラーになります。

7.3 数値ラッパークラスでも compareTo が使える

IntegerLong などでも、compareTo() を使えます。

Integer x = 10;
Integer y = 20;

if (x.compareTo(y) < 0) {
    System.out.println("x は y より小さい");
}

値比較と順序判定を同時に扱いたい場合、compareTo() は非常に便利です。

7.4 compareTo と equals の使い分け

混乱しやすいので、ここで整理します。

  • 等しいかどうかだけ知りたい
    equals()
  • 大小や並び順を知りたい
    compareTo()

用途が違うため、置き換えはできません。

7.5 ソート処理への自然なつながり

compareTo() は、次のような場面で自動的に使われます。

  • Collections.sort()
  • List.sort()
  • TreeSet / TreeMap
List<String> list = Arrays.asList("Banana", "Apple", "Cherry");
Collections.sort(list);

内部で compareTo() が呼ばれ、順序が決まります。

7.6 このセクションのまとめ

  • 比較演算子は 参照型の大小比較に使えない
  • 順序を比較したい場合は compareTo()
  • equals() と役割が違う点を意識する
  • ソート処理の基礎にもなる重要メソッド

次のセクションでは、
ここまでの内容を踏まえて よくあるミスを一気に整理します。

8. よくあるミス集(チートシート)

比較演算子まわりのトラブルは、ほぼパターン化しています。
このセクションを一度読んでおくと、レビューやデバッグが楽になります。

8.1 String を == で比較してしまう

if (str1 == str2) { ... } // NG
  • 原因:参照を比較している
  • 正解:str1.equals(str2) または Objects.equals(str1, str2)

8.2 Integer を == で比較して結果が変わる

Integer a = 1000;
Integer b = 1000;

a == b // false
  • 原因:Integerキャッシュ
  • 正解:a.equals(b)

8.3 double を == で比較して一致しない

0.1 + 0.2 == 0.3 // false になることがある
  • 原因:浮動小数点誤差
  • 正解:許容誤差で比較、または BigDecimal

8.4 null チェックを忘れて NullPointerException

str.equals("Java") // str が null だと即エラー
  • 正解:”Java”.equals(str) / Objects.equals(str, “Java”)

8.5 大小比較に比較演算子を使おうとする

if (str1 < str2) // コンパイルエラー
  • 正解:str1.compareTo(str2)

8.6 このセクションのまとめ

  • 比較演算子は 万能ではない
  • 型によって「正しい比較方法」が決まっている
  • 迷ったら「値か?参照か?順序か?」で考える

次はいよいよ、この記事の総まとめです。

9. まとめ:迷ったらこの基準で判断する

最後に、この記事の要点を一枚の指針としてまとめます。

9.1 比較方法の判断基準

  • プリミティブ型(int / boolean など)
    → 比較演算子(==, <, > など)
  • 参照型の内容比較(String / Integer など)
    equals() / Objects.equals()
  • 順序・大小比較(並び・ソート)
    compareTo() / Comparator

9.2 安全な比較を書くための意識

  • == は「同じ値」ではなく「同じモノ」を見る場合がある
  • null の可能性を常に意識する
  • 実装依存(Integerキャッシュなど)に頼らない

9.3 次に学ぶと理解が深まるトピック

  • 論理演算子(&& / ||
  • if 文・switch 文
  • Comparator を使ったカスタムソート
  • equals / hashCode の正しい実装

比較演算子は小さなテーマに見えますが、Javaのバグの温床になりやすい重要分野です。
ここで整理した考え方を身につけておくと、コードの安全性と可読性が大きく向上します。

FAQ

Q1. Javaで ==equals() は何が違うのですか?

== は同じインスタンス(参照)かどうかを比較します。
equals() はオブジェクトの内容が等しいかを比較します。
StringやIntegerの比較では equals() を使うのが基本です。

Q2. Stringの比較に == を使ってはいけないのはなぜですか?

文字列プールの仕組みにより、
たまたま true になる場合と false になる場合が混在するためです。
常に equals() を使うことで、意図どおりの比較ができます。

Q3. null が含まれる可能性がある場合はどう比較すればいいですか?

Objects.equals(a, b) を使うと、null安全に比較できます。
定数との比較なら "Java".equals(str) も安全です。

Q4. 文字列の大小比較はどうすればいいですか?

<> は使えません。
String.compareTo() を使って、戻り値で順序を判定します。

Q5. 比較演算子だけ覚えれば十分ですか?

プリミティブ型には十分ですが、
実務では equals()compareTo()、null安全な比較まで理解しておく必要があります。