📌

Javaのequalsと==について

2024/07/03に公開

今更ながら、きちんと説明できるだけ深堀したことなかったのでまとめ。

そもそも==演算子とは?

基本データ型(プリミティブ型)に対して使う場合、そのを比較する。
参照型(オブジェクト型)に対して使う場合、そのオブジェクトの参照:メモリアドレスを比較します。

Main.java
//プリミティブ型の比較
int a = 5;
int b = 5;
System.out.println(a == b); // true(プリミティブかつ値が同じだから)

//オブジェクト型の比較
String str1 = new String("Hello");
String str2 = new String("Hello");
System.out.println(str1 == str2); // false(異なるオブジェクトだから)

equalsメソッドとは?

オブジェクトの内容を比較するために使われる。
Objectクラスのデフォルトでは、以下のコードになっている。

Object.java
//Java:21
public boolean equals(Object obj) {
    return (this == obj);
}

特にStringクラスでは、equalsメソッドがオーバーライドされていて
文字列が同じかどうかを比較するように実装されている。

String.java
//Java:21
public boolean equals(Object anObject) {
    if (this == anObject) {
        return true;
    }
    return (anObject instanceof String aString)
            && (!COMPACT_STRINGS || this.coder == aString.coder)
            && StringLatin1.equals(value, aString.value);
}

自分でクラスを作成する場合、
equalsをオーバーライドして比較する内容を決めてあげる必要がある。

なぜStringに対して==を使ってはいけないのか?

よく入門書とかに書かれているString型の時にJavaでは==を使ってはいけないお話。
Stringクラスに対して==を使うと、オブジェクトの参照を比較することになる。
つまり、文字列の内容が同じであっても異なるオブジェクトとして作成された場合、
==falseを返してしまう。

Main.java
String str1 = new String("Hello");
String str2 = new String("Hello");
//中身が同じなのに、、、
System.out.println(str1 == str2); // false

特にStringに関して、
書き方によっては文字列リテラルがプールされるため[1]
プールされている参照が割当たった時は比較結果が上記と逆になる。

Main.java
//文字列リテラルをプールして参照した場合
String str1 = "Hello";
String str2 = "Hello";
System.out.println(str1 == str2); // true(同じリテラルを参照しているため)

//オブジェクトとしてそれぞれ定義した場合
String str3 = new String("Hello");
String str4 = new String("Hello");
System.out.println(str3 == str4); // false(異なるオブジェクトだから)

この挙動の違いが期待した結果と異なることが多いためバグの原因となる。

正しい文字列比較の方法

文字列の内容を比較する場合は、必ずequalsメソッドを使用すること。
正しく把握して使う分にはよいかと思うが、==にするメリットが思いつかないので
Stringはequalsを使うこととして覚えてしまった方がよさげ。
(もちろん、理由はキチンと把握しておいた方がよいが、、、)

脚注
  1. 細かい部分を書き始めると長くなるので簡単に、、、
    文字列はメモリ効率化のため
    特定の方法で作成するとメモリの特殊な場所に文字列を保存する。
    その後、同じ文字列を作成する場合、
    別の場所にメモリを確保するのではなく既にある文字列の場所を参照するようにメモリを持つイメージ ↩︎

Discussion