LoginSignup
0
0

More than 5 years have passed since last update.

Item 61: Prefer primitive types to boxed primitives

Posted at

61.ボクシングされたプリミティブ型(boxed primitive)より、プリミティブ型(primitive)を選択すべし

ボクシングされたプリミティブ型とプリミティブ型の違い

Item6でも述べられれているように、オートボクシングとオートアンボクシングにより、ボクシングされたプリミティブ型(以下boxed primitive)とプリミティブ型(以下primitive)の違いはあいまいになっている。
しかし、これらの2つには違いがあり、使用時にはその差異に気を付けながら使わなければならない。その違いは3つで、
* primitiveは値のみを持つが、boxed primitiveは値とは異なる形で参照を持つ。
* primitiveは機能する値しか持ちえないが、boxed primitiveはnull(nonfunctional value)があり得る。
* primitiveのほうが、時間的にも省メモリスペース的にも優れている。

boxed primitiveを使用した場合の誤り例

primitiveは値のみを持つが、boxed primitiveは値とは異なる形で参照を持つ

以下のコードは一見うまく比較をしてくれるメソッドにみえる。

// Broken comparator - can you spot the flaw?
Comparator<Integer> naturalOrder =
    (i, j) -> (i < j) ? -1 : (i == j ? 0 : 1);

しかし、このメソッドをnaturalOrder.compare(new Integer(42), new Integer(42))のように用いると、想定としては等しいので0を返すはずだが、実際には1を返す。
1つ目の判定(i < j)は、auto-unboxedされて正しく動作する。
一方、2つ目の判定(i == j)は、同値であることをみる、つまり、参照が同じであるかをみている。よって、この比較はtrueにはならず、結果として1が返却される。 boxed primitiveに対して==演算子を用いることはたいてい間違っている。

上記の誤りを防ぐためには、Comparator.naturalOrder()を用いるか、自身でcomparatorを書くのであれば、以下のようにprimitiveでの比較を行うようにする。

Comparator<Integer> naturalOrder = (iBoxed, jBoxed) -> {
    int i = iBoxed, j = jBoxed; // Auto-unboxing
    return i < j ? -1 : (i == j ? 0 : 1);
};

primitiveは機能する値しか持ちえないが、boxed primitiveはnull(nonfunctional value)があり得る

以下のプログラムでは、ヌルポが発生する。

package tryAny.effectiveJava;

public class BoxedPrimitive {
    static Integer i;

    public static void main(String[] args) {
        if (i == 42)
            System.out.println("Unbelievable");
    }

}

i==47のところで、Integerとintを比較している。
boxed primitiveとprimitiveが混じった処理においては、たいていの場合boxed primitiveがauto-unboxされる。
nullを参照しているオブジェクトをunboxするとヌルポが生じるが、それがここでは起きている。

primitiveのほうが、時間的にも省メモリスペース的にも優れている

以下は、Item6で扱ったプログラムである。

package tryAny.effectiveJava;

public class BoxedPrimitive2 {
    // Hideously slow program! Can you spot the object creation?
    public static void main(String[] args) {
        Long sum = 0L;
        for (long i = 0; i < Integer.MAX_VALUE; i++) {
            sum += i;
        }
        System.out.println(sum);
    }

}

box化、unbox化が繰り返し起こるため、これはローカル変数の型をlongにした場合よりも格段に遅い。

総括

3つの問題点があったが、最初の2つは結果がおかしくなり、最後の1つは性能が悪くなる。

boxed primitiveの使い時としては以下の場合がある。
* collectionの要素、キー、バリューとして使うとき。primitiveをcollectionに入れることはできないので使わざるを得ない。
* パラメータ化された型に用いるとき。例えば、ThreadLocal<int>という型は宣言できないが、ThreadLocal<Integer>は宣言できる。
* リフレクションでメソッドを参照するときはboxed primitiveを使う(?)。(Item65)

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0