3.

Javaの比較演算子|==とequals・compareToの違いと参照型の罠

編集
この記事の要点
  • 比較演算子は 2 つの値の大小・等価を判定し、結果を booleantrue / false)で返す
  • Java の比較演算子は == != < > <= >= の 6 種類
  • ==プリミティブ型では値の比較、参照型では参照(メモリ上のアドレス)の比較になる
  • 文字列の中身を比較したいときは String#equals、大小は String#compareTo を使う
  • if / while / 三項演算子 / for ループの継続条件など、制御構文の前提となる基礎演算子

比較演算子とは

Java における比較演算子(relational operator)は、左辺と右辺の値を比較してその結果を boolean 型の true / false で返す演算子です。if 文の条件、while ループの継続条件、三項演算子の評価式など、ほぼ全ての制御構文の中で使われます。

Java の比較演算子一覧

演算子意味結果
==左辺と右辺が等しいとき true5 == 5true
!=左辺と右辺が等しくないとき true5 != 3true
<左辺が右辺より小さいとき true3 < 5true
>左辺が右辺より大きいとき true5 > 3true
<=左辺が右辺以下のとき true5 <= 5true
>=左辺が右辺以上のとき true5 >= 5true

すべての結果は boolean 型なので、そのまま ifwhile の条件に置けます。

基本的な使い方

int score = 80;

if (score >= 60) {
    System.out.println("合格");
} else {
    System.out.println("不合格");
}

// while ループ
int i = 0;
while (i < 5) {
    System.out.println(i);
    i++;
}

// 三項演算子
String result = (score >= 60) ? "合格" : "不合格";

プリミティブ型と参照型での == の違い

Java で初心者がつまずく一番のポイントが == の挙動です。プリミティブ型int long double boolean など)では値そのものを比較しますが、参照型String や自作クラスなど)では「同じオブジェクト(同じアドレス)を指しているか」を比較します。

// プリミティブ型 ─ 値の比較
int a = 100;
int b = 100;
System.out.println(a == b);   // true

// 参照型 ─ アドレスの比較
String s1 = new String("hello");
String s2 = new String("hello");
System.out.println(s1 == s2);          // false (別オブジェクト)
System.out.println(s1.equals(s2));     // true  (中身が等しい)

文字列リテラルは「文字列プール」で共有されるため == が偶然 true になることがありますが、これに頼るのは禁物です。文字列・オブジェクトの等価比較は必ず equals を使うのが Java の鉄則です。

大小比較 ─ compareTo

文字列やオブジェクトの大小を判定したいときは compareTo を使います。0 なら等しい、負なら左辺が小さい、正なら左辺が大きい、と読みます。

String a = "apple";
String b = "banana";

int r = a.compareTo(b);
if (r < 0)      System.out.println("a < b");   // 出力される
else if (r > 0) System.out.println("a > b");
else            System.out.println("a == b");

論理演算子との組み合わせ

比較演算子は &&(AND)||(OR)!(NOT)と組み合わせて複雑な条件を組み立てるのが一般的です。

int age = 25;
boolean hasLicense = true;

// 18 歳以上 かつ 免許あり
if (age >= 18 && hasLicense) {
    System.out.println("運転できます");
}

// 子供 または シニア
if (age < 12 || age >= 65) {
    System.out.println("料金割引");
}

よくある落とし穴

NG原因OK
str1 == str2参照(アドレス)比較になっているstr1.equals(str2)
if (a = 5)代入演算子になっている(Java はコンパイルエラー)if (a == 5)
0.1 + 0.2 == 0.3浮動小数点誤差で false になるMath.abs((0.1+0.2) - 0.3) < 1e-9
Integer x=128, y=128; x==yオートボクシングで別オブジェクトx.equals(y) または x.intValue()==y.intValue()

Integer キャッシュの罠

Java のラッパー型 Integer-128 〜 127 の範囲だけは内部キャッシュされており、同じ値なら同じインスタンスが返ります。この範囲を超えるとキャッシュされず、==false になります。

Integer a = 100, b = 100;
System.out.println(a == b);   // true  (キャッシュ範囲内)

Integer x = 200, y = 200;
System.out.println(x == y);   // false (キャッシュ外なので別オブジェクト)
System.out.println(x.equals(y)); // true

「動いていたのに本番だけ壊れた」の典型例なので、ラッパー型同士の等価比較は必ず equals を使う癖を付けてください。

null との比較

参照型で null かどうかを判定する唯一の方法が == です。equalsnull に対して呼ぶと NullPointerException が出るため、左右どちらが null か分からない場合は Yoda 記法Objects.equals を使います。

String s = null;

// null 判定は == のみ
if (s == null) System.out.println("空です");

// NG: s が null だと NPE
// if (s.equals("hello")) ...

// OK: 文字列リテラルから呼ぶ(Yoda)
if ("hello".equals(s)) System.out.println("hello");

// OK: java.util.Objects を使う(推奨)
import java.util.Objects;
if (Objects.equals(s, "hello")) System.out.println("hello");

演算子の優先順位

比較演算子は算術演算子より低く、論理演算子より高い位置にあります。よって a + b > c(a + b) > c と評価され、a > 0 && b < 10(a > 0) && (b < 10) と評価されます。可読性のため、複雑な条件は明示的に括弧で囲むのがおすすめです。

関連

編集
Post Share
子ページ

子ページはありません

同階層のページ
  1. 算術演算子
  2. 代入演算子
  3. 比較演算子
  4. 論理演算子

最近更新/作成されたページ