JavaのBooleanクラスの==比較

概要
JavaはObject同士を==で比較した際の戻り値がちょっと厄介です。

よくあるのが、Stringを==比較した場合に、同じ文字列が入ってるのに結果がtrueになったりfalseになったりするというものです。

では、BooleanのObjectを==で比較した場合は、どんな感じの結果になるのでしょう。
使用しているJavaのバージョン
JRE 1.6.0_07
実際に比較してみた
コード内で使っているBoolean.TRUEというのは、Booleanが持っているpublic static finalでtrueなBooleanオブジェクトです。

    /** Boolean Objectをいろいろ比較してみる */
    Boolean b;

    // new Booleanすると、新しいObjectが生成される
    // なので、==比較の結果はfalseになる
    b = new Boolean(true);
    System.out.println( b == Boolean.TRUE );
      // => false

    // equalsで比較すれば、もちろんtrue
    b.equals( Boolean.TRUE );
      // => true

    // valueOfを使用すると、Boolean.TRUEと同じObjectが返されるようです
    // なので、Boolean.TRUEと==比較しても、trueになります
    // 新しいObjectが生成されないので地球に優しいですね
    b = Boolean.valueOf(true);
    System.out.println( b == Boolean.TRUE );
      // => true

    // Autoboxingすると、valueOfと同義で扱われるので、trueになります
    // Integerでもそうでしたが、下手にnewとか書くよりも、
    // autoboxingで任せておけば、勝手に一番軽いの選んでくれるような気がします
    b = true;
    System.out.println( b == Boolean.TRUE );
      // => true
    
    // 文字列でvalueOfしてもOKです
    b = Boolean.valueOf("true");
    System.out.println( b == Boolean.TRUE );
      // => true
          
こういう結果になる理由
JavaのBooleanは、Boolean.TRUEとBoolean.FALSEという定数を持っています。これは中の人的には、こんな風に定義しています。

    public static final Boolean TRUE = new Boolean(true);

    public static final Boolean FALSE = new Boolean(false);
          
つまり、実行する前からTRUEとFALSEのObjectを1つずつ定数としてnewして保持してしまっているわけです。

Boolean.valueOf(boolean)を使用した場合は、こんな感じで値が返されます。


    public static Boolean valueOf(boolean b) {
        return (b ? TRUE : FALSE);
    }
          
Boolean内のメソッドなので、Boolean.TRUEと書かずにそのまま「TRUE」と書いてますが、見ての通り、Boolean.TRUEかBoolean.FALSEを返しています。

こうしてみていると、new Booleanと書かなければ、Booleanは==で比較してもOKなような気もしなくもないです。

Booleanのequalsメソッドはこんなことをしています。

    public boolean equals(Object obj) {
        if (obj instanceof Boolean) {
        return value == ((Boolean)obj).booleanValue();
        } 
        return false;
    }
          
大した処理ではないですが、==の比較の方が若干速くなりそうです。

まぁ、そんなわずかの処理の為に、==比較みたいなバグの元になるような記述を入れるのはJavaらしくないですが。
実際にどの程度のメモリ使用量の差があるのだろう
毎回 new Booleanするのと、valueOfを使うのとでは、どの程度のメモリ使用量の差が出るのでしょう。

試しにRuntime.getRuntime.freeMemory()を使用して、使用量を覗いてみましょう。

    Runtime runtime = Runtime.getRuntime();

    // new Booleanを1万個ほど生成してみます
    Boolean b1[] = new Boolean[10000];

    // 追加前のメモリ使用量を持っておきます
    long mem1 = runtime.totalMemory() - runtime.freeMemory();
    for( int i = 0; i < b1.length; i++ ) {
        b1[i] = new Boolean( true );
    }
    // 生成後のメモリ使用量を取っておきます
    long mem2 = runtime.totalMemory() - runtime.freeMemory();
    
    // Autoboxingで1万個ほどBoolean配列を埋めてみます
    Boolean b2[] = new Boolean[10000];

    // 追加前のメモリ使用量を持っておきます
    long mem3 = runtime.totalMemory() - runtime.freeMemory();
    for( int i = 0; i < b2.length; i++) {
        b2[i] = true;
    }
    // 生成後のメモリ使用量を取っておきます
    long mem4 = runtime.totalMemory() - runtime.freeMemory();

    // newでBooleanを1万個生成した際に増加したメモリ量
    System.out.println( "使用量 : " + ( mem2 - mem1 ) );
      // => 使用量 : 152496

    // Autoboxingで1万個のBooleanを埋めた際に増加したメモリ量
    System.out.println( "使用量 : " + ( mem4 - mem3 ) );
      // => 使用量 : 0

    // スコープ内だし大丈夫なはずだけど、念のため、ガベージコレクション封じ的な無駄処理
    System.out.println( b1 );
    System.out.println( b2 );
          
さて、結果が出ました。

newした場合152496バイト使用
Autobxing(valueOfと同義)の場合0バイト使用

はい、大変気持ちの良い結果ですね。新しくObjectを生成するわけじゃないので、0になるのは当然と言えば当然なのですが。
総評
というわけで、Booleanの==に関するちょっとしたお話でした。

割とどうでも良い話ですが、何かの閑話休題くらいには使えそうなネタかなぁと思います。

尚、この説明文はMOTHERのプレイ動画を見ながら作成しております。おっ、今、ちょうど歌い始めました。なので誤字等があるかもしれませんが、その時はブログのコメントにご連絡ください。

http://blog.mwsoft.jp/article/30901931.html