0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Effective Java 9.61: ボクシングされた基本データよりも基本データ型を選ぶ

Posted at

9.61: ボクシングされた基本データよりも基本データ型を選ぶ

結論

可能な限り プリミティブ型(int, long, double など)を使い、ボクシングされたラッパ型(Integer, Long, Double 等)を避ける。
理由は主に
(1)性能(オートボクシング/アンボクシングのコスト・ガベージ発生)、
(2)メモリ(ラッパーはオブジェクトなので大きい)、
(3)安全性(null のアンボクシングで NullPointerException が出る)、
(4)比較の誤解(== と参照等価)
──これらがバグや効率低下の原因になるため。
※ただし generics/コレクションの要件や API 境界でどうしてもオブジェクトが必要な場合は仕方ない(そのときは意図を明確に)。


良い例

プリミティブを使って安全かつ効率的に書く例。配列・プリミティブストリームを活用。

// 1) 純粋な数値ループはプリミティブで
long sum(long[] values) {
    long s = 0L;
    for (long v : values) {
        s += v;
    }
    return s;
}

// 2) Java 8+ のプリミティブストリームを使う(ボクシングを避ける)
import java.util.stream.IntStream;

int sumRange(int n) {
    // IntStream.rangeClosed はプリミティブ int を扱う -> ボクシング不要
    return IntStream.rangeClosed(1, n).sum();
}

ポイント:

  • ループ内やホットパスでは必ずプリミティブを使う(オートボクシングでオブジェクトが大量に生成されるのを避ける)
  • コレクションを使う必要があるなら、可能な限りプリミティブ向けAPI(IntStream / LongStream)やサードパーティのプリミティブコレクションを検討する

悪い例

ボクシングによる落とし穴(性能・バグ)の典型。

// NG: tight loop で List<Integer> を使うと大量のオブジェクトが生まれる
List<Integer> list = new ArrayList<>();
for (int i = 0; i < 1_000_000; i++) {
    list.add(i); // autoboxing: int -> Integer, 1_000_000 個の Integer を割当て
}
long sum = 0L;
for (Integer x : list) {
    sum += x; // unboxing: Integer -> int(さらに毎回オーバーヘッド)
}

// NG: null をアンボクシングして NPE
Integer maybeNull = null;
int primitive = maybeNull; // NullPointerException

// NG: == による誤った比較(参照等価と値等価の混同)
Integer a = 128;
Integer b = 128;
System.out.println(a == b);      // false(キャッシュ外の値は別オブジェクト)
System.out.println(a.equals(b)); // true(値は等しい)

問題点:

  • ホットパスでのオートボクシングは CPU とメモリを浪費する(ガベージ生成、キャッシュ負荷)
  • Integer 等を null にすることで、意図せず NullPointerException が発生する
  • ==equals() の混同でロジックのバグが生まれる(キャッシュ範囲によって結果が変わる)

まとめ

  • ホットパス/大量データはプリミティブを使う:
    ループ、集計、数値配列等は int/long/double で。
     
  • API 境界ではラッパーが必要な場合がある:
    ジェネリクスやコレクション(List<T>)ではオブジェクト型が必須。
    ただし内部処理はプリミティブで行い、ボクシングは境界で最小化する。
     
  • プリミティブ向け API を使う:
    IntStream / LongStream / DoubleStreamArrays のプリミティブ overload を活用。
     
  • null をアンボクシングしない:
    ラッパーを使うなら null の可能性を設計で排除するか OptionalInt 等のプリミティブ Optional を検討する。
     
  • 比較は equals を使う:
    ラッパー同士の比較で == を使わない(参照等価に注意)。
     
  • 大規模コレクションではプリミティブ専門ライブラリを検討:
    memory- and GC-sensitive なケースでは fastutil / Trove など(プロジェクトポリシーに合わせて)。
     
  • 測定して判断する:
    パフォーマンス疑問があるならベンチマーク(JMH)で確かめる。
    見積りだけで最適化しない。
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

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?