Javaで値を比べようと==
やequals()
を使ってみたら、思わぬ結果が返ってきて混乱したことはありませんか?
私も最初、文字列同士を==
で比べて「同じはずなのにfalse!」と何度も悩みました。
本記事では、==
とequals()
の違いから、独自クラスでのequals()
実装、プリミティブとラッパーの比較、文字列比較メソッドの選び方、コレクション内での重複判定まで、やさしい例とともに徹底解説します。
「この記事を読んだら、オブジェクト比較の謎がスッと解けた!」という声が多数。
new String("a")==new String("a")
が false
になる理由や、ArrayList.contains()
の動作原理もクリアに理解できます。
今すぐ読み進めて、バグのない安全な比較コードを書けるようになりましょう!
== と equals() の違いがわからない
==
は「同じ場所か」を比べ、equals()
は「中身が同じか」を比べます。
Javaでオブジェクト同士を==
で比べると、同じインスタンスでなければfalse
になります。equals()
はクラスごとに中身の比較ロジックを書けるため、値の同一性を判断したいときに使います。
具体例を見てみましょう!
String a = new String("Hello");
String b = new String("Hello");
System.out.println(a == b); // false、別オブジェクト
System.out.println(a.equals(b)); // true、中身は同じ
Integer x = 128;
Integer y = 128;
System.out.println(x == y); // false、ボクシングで別オブジェクト
System.out.println(x.equals(y)); // true、中身は同じ
オブジェクトを比較するときは必ずequals()
を使い、==
はプリミティブ型や参照同一性のチェックに限定しましょう。
equals() をオーバーライドする方法がわからない
自作クラスで中身比較を行うには、equals()
と hashCode()
を正しくオーバーライドする必要があります。
コレクション(Set, Mapなど)はequals()
とhashCode()
で要素の同一性を判断します。これを書かないと、同じ値を持つオブジェクトが別扱いされてしまいます。
例えば!
public class Person {
private String name;
private int age;
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Person)) return false;
Person p = (Person) o;
return age == p.age && name.equals(p.name);
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
}
これで new Person("Alice",20).equals(new Person("Alice",20))
は true
になります。
equals()
と hashCode()
はセットでオーバーライドし、中身比較のルールをクラス定義に組み込みましょう。
プリミティブ型とラッパークラスの比較ミスをなくす
int
なら ==
、Integer
なら equals()
で比較します。
ラッパークラスはオブジェクトなので、==
は参照比較になります。ヌル値を扱うとき、equals()
ではNullPointerException
に注意が必要です。
int a = 5;
int b = 5;
System.out.println(a == b); // true, プリミティブ比較
Integer x = 5;
Integer y = 5;
System.out.println(x == y); // true (キャッシュ範囲内)
Integer m = 128;
Integer n = 128;
System.out.println(m == n); // false (キャッシュ外)
System.out.println(m.equals(n)); // true
ヌル値を安全に比較するには、Objects.equals(x, y)
がおすすめです。
プリミティブは ==
、ラッパーは equals()
で、ヌル対策には Objects.equals()
を使いましょう。
文字列比較で equalsIgnoreCase() と compareTo() の使い分け
大文字小文字を無視するときは equalsIgnoreCase()
、辞書順に並べたいときは compareTo()
を使います。
equalsIgnoreCase()
は文字列同士の同一性を大文字小文字を区別せずにチェック。compareTo()
は辞書順比較を行い、ソートや範囲チェックに適しています。
具体例を見てみましょう!
String s1 = "Java";
String s2 = "java";
System.out.println(s1.equalsIgnoreCase(s2)); // true
String[] arr = {"banana","Apple","cherry"};
Arrays.sort(arr); // デフォルトは compareTo()、大文字優先
System.out.println(Arrays.toString(arr));
大文字小文字無視ソートは String.CASE_INSENSITIVE_ORDER
を使えます。
同一性チェックか順序比較かでメソッドを選び、使い分けると文字列操作が正確になります。
コレクションの要素比較と重複判定を理解する
List.contains()
や Set
の重複判定は equals()
と hashCode()
に依存します。
コレクション内部では、要素同士を equals()
で比べ、ハッシュ構造なら hashCode()
も使います。これが未実装・誤実装だと重複判定が壊れます。
例えば!
Set<Person> set = new HashSet<>();
set.add(new Person("Bob",30));
set.add(new Person("Bob",30));
System.out.println(set.size()); // equals/hashCode 未実装なら2, 実装済みなら1
コレクションを使う前に、自作要素クラスの equals()
と hashCode()
を確実に実装しましょう。
まとめ:等価判定を正しく使い分けてバグを防ごう
- 参照比較と値比較:
==
はアドレス、equals()
は中身を比べる。 - equals/hashCode オーバーライド:自作クラスの中身比較ルールを定義。
- プリミティブ vs ラッパー:基本は
==
vsequals()
、Objects.equals()
でヌル対策。 - 文字列比較メソッド:同一性は
equalsIgnoreCase()
、順序比較はcompareTo()
。 - コレクション重複判定:
contains()
やSet
は equals/hashCode に依存。
これらを理解し、適切な比較方法を選べば、等価判定に起因するバグをぐっと減らせます。今日学んだことをコードで試し、安全で正確なJavaプログラムを書きましょう!