比較演算子の全体像
Java の比較演算子は「式の真偽を判定して boolean を返す」ための基本ツールです。代表は ==、!=、<、>、<=、>= で、数値や文字の大小、同値性を判定します。重要なのは「プリミティブ(値そのもの)と参照型(オブジェクト)で意味が違う」こと、そして浮動小数点の比較には誤差や NaN の罠があることです。
基本の比較演算子と整数・char の挙動
整数の比較
整数同士の比較は直感どおりに動きます。結果は boolean です。
int a = 7, b = 3;
System.out.println(a == b); // false
System.out.println(a != b); // true
System.out.println(a > b); // true
System.out.println(a <= b); // false
Java「演算の後に比較」する場合は、演算の型(int と long の混在など)が意図通りかを確認しましょう。型が広い方へ自動昇格されます。
char の比較(コードポイント順)
char は UTF-16 のコード単位です。大小比較はコード値順で行われます。
char x = 'A', y = 'a';
System.out.println(x < y); // true('A' のコードが 'a' より小さい)
Java絵文字などサロゲートペアが必要な文字は char 1 つに収まらないため、大小比較は String の compareTo やコードポイント API を使う方が安全です。
浮動小数点の比較と NaN/Infinity(重要ポイントの深掘り)
誤差を踏まえた比較
浮動小数点(double/float)は二進小数で誤差が付きやすく、完全一致比較は危険です。許容誤差(イプシロン)を使うのが定石です。
double r = 0.1 + 0.2; // 0.30000000000000004 など
double eps = 1e-9;
boolean eq = Math.abs(r - 0.3) < eps;
System.out.println(eq); // true(誤差内として同値扱い)
Java「計算結果を表示だけ丸めたい」場合は printf("%.2f")、「計算自体を厳密にしたい(金額など)」場合は BigDecimal を使います。
NaN と Infinity の比較ルール
double/float には特別値があります。ゼロ割りや未定義計算で発生します。
System.out.println(1.0 / 0.0); // Infinity
System.out.println(0.0 / 0.0); // NaN
System.out.println(Double.NaN == Double.NaN); // false(NaN は何とも等しくない)
System.out.println(Double.isNaN(Double.NaN)); // true(判定は専用メソッド)
Java比較前に Double.isNaN(x) や Double.isInfinite(x) でガードし、異常値を明確に扱うのが安全です。
参照型の比較:== と equals の違い(重要ポイントの深掘り)
参照の同一性(==)と内容の同値性(equals)
== は「同じインスタンスか」を比較し、equals は「内容が同じか」を比較します。文字列やコレクションの比較は必ず equals を使うのが原則です。
String a = "hi";
String b = new String("hi");
System.out.println(a == b); // false(別インスタンス)
System.out.println(a.equals(b)); // true(内容は同じ)
Javaequals をオーバーライドするクラスでは、hashCode も整合させることが契約です。マップやセットでの動作に直結します。
ラッパークラスの罠(Integer など)
Integer などのラッパー型はオブジェクトです。== は参照比較になるため、意図通りの結果にならないことがあります(値キャッシュの影響もある)。
Integer x = 128;
Integer y = 128;
System.out.println(x == y); // false(参照が違うことが多い)
System.out.println(x.equals(y)); // true(値比較)
Javaプリミティブにアンボックスして比較するか、equals を使うのが安全です。
文字列の順序比較と部分一致
文字列の順序比較(辞書順)
String.compareTo は辞書順で比較します。0 が同値、負が「小さい」、正が「大きい」を意味します。
System.out.println("Apple".compareTo("Banana")); // 負(Apple の方が前)
System.out.println("Hi".compareTo("Hi")); // 0(同値)
Java大小比較はロケールの影響を受けない単純なコード値順です。言語学的な順序が必要なら Collator を使います。
部分一致・前方/後方一致
部分一致は contains、前方一致は startsWith、後方一致は endsWith を使います。
String s = "hello.java";
System.out.println(s.contains("java")); // true
System.out.println(s.startsWith("hello")); // true
System.out.println(s.endsWith(".java")); // true
Java大小無視の比較は equalsIgnoreCase。ロケール依存のケースでは toLowerCase(Locale.ROOT) などで正規化してから比較します。
BigDecimal と厳密比較、整数の境界(重要ポイントの深掘り)
BigDecimal は compareTo を使う
金額などの厳密比較は BigDecimal。equals はスケール(小数点桁)も比較するため、「1.0」と「1」は等しくありません。値比較には compareTo を使います。
import java.math.BigDecimal;
BigDecimal a = new BigDecimal("1.0");
BigDecimal b = new BigDecimal("1");
System.out.println(a.equals(b)); // false(スケールが違う)
System.out.println(a.compareTo(b) == 0);// true(数値として同値)
Java境界値とオーバーフローの前提チェック
大小比較の前に「範囲に収まるか」を確認しておくと安全です。int の加算は静かにオーバーフローします。大きな値なら long を選び、必要なら境界チェックを入れます。
int max = Integer.MAX_VALUE;
int n = max - 10;
System.out.println(n < max); // true(境界の直前)
Javanull 安全な比較とユーティリティ
null を含む同値判定
Objects.equals(a, b) はどちらかが null でも安全に内容比較できます。手書きの a != null && a.equals(b) を簡潔に置き換えられます。
import java.util.Objects;
String a = null, b = "hi";
System.out.println(Objects.equals(a, b)); // false
System.out.println(Objects.equals(null, null)); // true
Java順序比較で null を許容するなら、先に null チェックのポリシー(null を最小/最大/不許可)を決め、コードに明示します。
実用例で身につける
例 1: スコア評価(しきい値の比較)
public class Grading {
public static void main(String[] args) {
int score = 78;
String grade = score >= 80 ? "A" :
score >= 70 ? "B" :
score >= 60 ? "C" : "D";
System.out.println(grade);
}
}
Java比較の順序は「上位から下位へ」。しきい値は重複のないように並べると読みやすく安全です。
例 2: 浮動小数点の近似比較
public class Approx {
public static void main(String[] args) {
double a = 0.1 + 0.2;
double b = 0.3;
double eps = 1e-9;
boolean same = Math.abs(a - b) < eps;
System.out.println(same); // true
}
}
Java近似比較を小さなユーティリティで統一すると、誤差の扱いがブレません。
例 3: 文字列の並び替え(比較関数)
import java.util.*;
public class SortByName {
public static void main(String[] args) {
List<String> names = Arrays.asList("sato", "Abe", "tanaka");
names.sort(String::compareTo); // 辞書順
System.out.println(names); // [Abe, sato, tanaka]
}
}
Java比較関数は「0/正/負」を返す compareTo を使うのが標準形です。大小の定義を一箇所にまとめると、バグの芽を摘めます。
仕上げのアドバイス(重要ポイントのまとめ)
== はプリミティブでは値比較、参照型では同一インスタンス比較。内容比較は常に equals(金額は BigDecimal.compareTo)。浮動小数点はイプシロンで近似比較し、NaN/Infinity は専用メソッドで判定。ラッパー型は == を避け、Objects.equals で null 安全に。比較の順序としきい値を明確に書き、境界条件をテストで覆う。これだけ押さえれば、比較に関する初学者のつまずきはほぼ消えます。
