2009年01月23日

JavaのIntegerの比較

今日、Javaの5でコーディングをしていて気づいたこと。

問題1
Integer int1 = new Integer("10");
Integer int2 = new Integer("10");
System.out.println(int1 == int2);

この実行結果は?


問題2
Integer int1 = Integer.valueOf(10);
Integer int2 = Integer.valueOf(10);
System.out.println(int1 == int2);

この実行結果は?


問題3
Integer int1 = Integer.valueOf("10");
Integer int2 = Integer.valueOf("10");
System.out.println(int1 == int2);

この実行結果は?


問題4
Integer int1 = Integer.parseInt("10");
Integer int2 = Integer.parseInt("10");
System.out.println(int1 == int2);

この実行結果は?


問題5
Integer int1 = Integer.parseInt("130");
Integer int2 = Integer.parseInt("130");
System.out.println(int1 == int2);

この実行結果は?


問題1 : false
newした場合は、必ず参照は別になる。

問題2 : true
Integer.valueOf(int) を使用した場合は、キャッシュが使用される。int1でキャッシュされたメモリ領域をint2が参照する形になるので、比較結果はtrueになる。

問題3 : false
Integer.valueOf(String) を使用した場合は、キャッシュが使用されない。内部的にはparseIntした後、new Integer(int)している。

問題4 : true
parseIntで生成されたintがAutoboxingでIntegerになっている。Autoboxingは内部的にはInteger.valueOf(int)で変換しているので、問題2と同じ理由でtrueになる。

問題5 : false
Integerのキャッシュは、-128〜127までの値に対して行われているので、130は普通に新しいメモリ領域が確保される。


問題3の答えがfalseになるということを、今日まで知らなかった。parseIntした後、valueOfせずにnewしてるのはなぜだろう。Javaの6で動かしても同じ結果になったので、きっと理由があると思うのだけど。
posted by MW at 01:13| Comment(5) | TrackBack(0) | 所感 | このブログの読者になる | 更新情報をチェックする
この記事へのコメント
まるでsjc/pの問題のような内容に、今受ける準備をしているので思わず飛び付いてしまいました(^_^;)

たぶん、問三に関してはボックス化が影響しています。
宣言がイントのラッパークラスなので、仮にバリューオブでつなげても新しいオブジェクトを作ってしまうのです。
しかも、このボックス化は自動ボックス化といいまして、コンパイラがプリミティブとラッパーが違いを把握してボックスしなおしてくれるという優れ物?(迷惑?)な機能です。
その為、問題のコードを1.4以前のコンパイラで処理してもコンパイルエラーになります。
Java5では基本的なJVMの動作というかコードの解釈を変えず、コンパイラが便利機能を盛り込んで、従来のコードに直して、更にバイトコードに直す荒業をやってのけます。
c#の真似をしてJava5で有名になったジェネリックスも実はコンパイラが内部的にコードにキャストを付け加えて従来のコードに直しています。
つまり本当のバイトコードにジェネリックスの概念は無いわけです。(^_^;)
ですからJavaの試験も1.4の1.5間にはかなりの難易度の差があります(^_^;)
だんだんとややこしくなりますJavaです(^_^;)
Posted by hp_rc at 2009年01月23日 20:01
Integer.valueOf(String):Integer
http://java.sun.com/javase/ja/6/docs/ja/api/java/lang/Integer.html#valueOf(java.lang.String)
は、もともとInteger型を戻り値とするメソッドなのでオートボクシングは関係ありません。

int型をInteger型に代入する場合などに発生するオートボクシングですが、この場合はバイトコード的にはInteger.valueOf(int)が用いられます。
問題5の解答に書いてあるとおり、このメソッドは-128〜127の範囲の値に対してキャッシュを持っており同一のインスタンスを返します。
オートボクシングに際して常に新しいオブジェクトがnewされるわけではありません。

ちなみに調べると分かることですが、
C#のジェネリクスはC#2.0からで2005年11月。
JavaのジェネリクスはJava5からで2004年9月。
言語へのジェネリクス搭載はJavaが1年以上先行します。
Posted by nagise at 2009年01月23日 21:44
ジェネリクス自体が提案されたのは JSR-14 ですから、1999 年のことですね。C# は 2001 年登場ですから、それより少し前のことになります。
http://www.jcp.org/en/jsr/detail?id=14
Posted by SiroKuro at 2009年01月24日 00:19
どうも。このごろあまりコメントする時間が無いのですが、このブログいつも本当に楽しませてもらっています。

去年から仕事でWebDevelopmentする機会が多くなって、今年に入ってから久しぶりにTomcat使っています。Integerはnullになる場合がある場合なんかによく使っていたのですが、==オペレータがcompareToをCallしていないってことすら知りませんでした・・(危ない危ない・・)

Posted by 宗市 at 2009年02月19日 23:40
どもっす。そういえば自分も最近似たようなミスで、Listから値をremoveする時に間違って引数にIntegerを使っていて、remove(int)のつもりがremove(Object)が呼ばれてさっぱり意図した要素が消えないとかやってました。精進します。
Posted by MW at 2009年02月20日 23:20
コメントを書く
お名前:

メールアドレス:

ホームページアドレス:

コメント:

この記事へのトラックバックURL
http://blog.seesaa.jp/tb/113018110

この記事へのトラックバック
×

この広告は1年以上新しい記事の投稿がないブログに表示されております。