/home/by-natures/dev*

データ界隈で働くエンジニアとしての技術的なメモと、たまに普通の日記。

Integer と Long を equals で比較すると false になる

タイトルを見れば Java に携わっている方ならすぐに察しが付くかと思うのですが、実際に動いているコードでこの問題が発生すると中々気付かなかったのでメモとして共有します。

数値型のオブジェクト同士を == で比較してはいけない

Java において、オブジェクト化されている(ボクシングされている)数値型を == で比較するとオブジェクトの比較になって、値が同じでも false になります。

Integer i1 = new Integer(1);
Integer i2 = new Integer(1);
System.out.println(i1 == i2); // false

(本来は new ではなく、Integer.valueOf() で Integer オブジェクトを生成すべきですが、-128 ~ 127 の範囲だと IntegerCache が効いて i1 == i2 が true となるため、分かりやすさのために new しています)

ちなみに、片方がプリミティブの場合は unboxing されるため、プリミティブ同士の比較となって true となります:

Integer i1 = new Integer(1);
int i2 = 1;
System.out.println(i1 == i2); // true
System.out.println(i2 == i1); // 逆でも true

equals メソッドなら大丈夫?

同じ型を equals で比較

オブジェクト同士の数値型を比較するためには、equals メソッドを用います:

Integer i1 = new Integer(1);
Integer i2 = new Integer(1);
System.out.println(i1.equals(i2)); // true

たまにメソッドを呼び出す変数(ここでは i1)が null だったりするので、ぬるぽで落ちたりするのはよくある話ですが…

違う型を equals で比較

さて、上の例は Integer 同士の比較でした。ここで、Integer と Long を equals で比較するとどうなるでしょうか?

Integer num_int  = new Integer(1);
Long    num_long = new Long(1);
System.out.println(num_int.equals(num_long)); // false

実は、これは false になります!equals メソッドは変数同士の型が等しいかどうかをチェックしている ため、型が等しくない Integer と Long だと、その時点で false となってしまいます。

Java の API ドキュメントでInteger クラスを見てみると、equals メソッドでは次のように定義されており、引数も Integer である必要があることが分かります:

equals public boolean equals(Object obj) このオブジェクトを指定されたオブジェクトと比較します。結果が true になるのは、引数が null ではなく、このオブジェクトと同じ int 値を含む Integer オブジェクトである場合だけです。

Integer と Long を比較するには、型を合わせて比較する必要があります:

Integer num_int  = new Integer(1);
Long    num_long = new Long(1);
System.out.println(num_long.equals(num_int.longValue())); // true

厳密に言えば、longValue() はプリミティブの long を返しているので、ここで long → Long の boxing が起きて、Long 同士での比較となります。Long の intValue() でもよいですが、int の範囲に収まらない場合もあるので、int を long に昇格する方が安全でしょう。

(追記)equals だと long → Long の boxing が発生するので、Long → long で unboxing してプリミティブで比較した方が効率がよいです。

Integer num_int  = new Integer(1);
Long    num_long = new Long(1);
System.out.println(num_long == num_int.longValue()); // true

余談:Ideone.com が便利

Java のようなコンパイル型言語で、上記サンプルのようなスニペットを動かすのに、Ideone.com が便利です。テキストエリアにコードを打ちこんでボタンを押すと、その場でコンパイル・実行してくれます。

(画像をクリックで Ideone.com へ)

ideone