あれこれ備忘録@はてなブログ

勉強したことやニュースや出来事を備忘録として書いていきます

このブログには広告が含まれます

オートボクシングの落とし穴

Integer i1 = 1; Integer i2 = 1; Integer i3 = 1000; Integer i4 = 1000; System.out.println(i1 == i2); System.out.println(i3 == i4);
これは最初のprintlnはtrueになり、二つ目の出力はfalseになります。 何故でしょう。 それはオートボクシング(auto-boxing)という機能のせいだそうです。 javaオブジェクト指向プログラミング言語ですが、すべてをオブジェクトとして扱うとパフォーマンスの低下を招くなどの理由でintなど一部の変数の型としてプリミティブ型というオブジェクトではない型の変数を扱えるようにしてあります。 byte・short・int・long・float・double・char・booleanがプリミティブ型です。 これをオブジェクトとして扱うときにはint型の場合、
Integer intObj1 = new Integer(100); Integer intObj2 = Integer.valueOf(2);
などといった形で100や2の値を持つIntegerクラスのオブジェクトを作って、これを例えばリストに格納したりします。 しかし、結構これは面倒です。 これを使ってもう一度整数の値として取り出して、計算などに使おうと思ったときには
int primitiveInt1 = intObj1.intValue();
などとします。 別のInteger型の変数に値だけ代入する場合には、
intObj2 = Integer.valueOf(intObj2.intValue());
これを計算で必要になる度にするのは面倒ですよね。 これを
intObj2 = primitiveInt1
のようにできるのがオートボクシング機能です。 しかし、これが落とし穴になるらしいのです。 もう一度見てみましょう。
Integer i1 = 1; Integer i2 = 1; Integer i3 = 1000; Integer i4 = 1000; System.out.println(i1 == i2); System.out.println(i3 == i4);
(i1 == i2)はtrueになるのに、(i3 == i4)はfalseになる。 これは何故か。 実は、そもそも(i1 == i2)も本来はfalseになるはずなのです。 (i1 == i2)というのはi1というオブジェクトとi2というオブジェクトは同じか?という判定を行っています。 i1とi2は別のオブジェクトなので本来はfalseなのですが、オートボクシングが効くとキャッシュされたIntegerが使われるそうで、 最初の
Integer i1 = 1
のときに、Integer.valueOf(1)で作られて返されたInteger型がキャッシュされていて、i2についても同じように定義されているので、このキャッシュされたものが使われるらしいのです。 だからこの場合、i1とi2は同じオブジェクトになるということです。 では、もう一方の(i3 == i4)はどうかというと、キャッシュが効く範囲がintの場合、-128から127なのでその範囲外の場合は別のオブジェクトとして定義されてしまいます。 その結果、(i3 == i4)はfalseになるのですね。 そういえば、今はどうか分かりませんが、以前はjavaの高速化手法として-128から127の範囲では
i++
よりも
i = i + 1
のほうが高速であるというのがありました。これはJVMで動くバイトコードが違うからだったと思います。 さて、もとの問題ですが、そもそも==をこの様に使うのは問題なので
i1.equals(i2);
のようにすると値を比較する方法としては正しく動きます。 Javaパフォーマンステクニック―サーバサイドプログラミングの最適化 (Java Performance and Scalability Vo) Javaパフォーマンス戦略―高速プログラムの高速開発のためのイディオム集 Javaプラットフォームパフォーマンス―コードレベルのチューニングと開発プロセスへの統合 (The Java Series) プロフェッショナルAndroidゲームプログラミング JavaとCによるハイパフォーマンスなゲームの作り方