LoginSignup
0
1

More than 3 years have passed since last update.

【Effective Javaを読む】 第2章 項目5 『不必要なオブジェクトの生成を避ける』

Last updated at Posted at 2020-07-31

不必要なオブジェクトの生成を避ける

タイトルの通り。オブジェクトを不用意に生成しまくることはパフォーマンスの劣化につながる。
ちょっと工夫して無駄なオブジェクトを生成しない書き方をしましょうって話。

用語集

自動ボクシング

Javaではプリミティブ型(基本型)と参照型の2つの型がある。
プリミティブ型、例えばintをコレクションに置くことはできない。
コレクションはオブジェクト参照だけを保持できるため、プリミティブ値は適切なラッパークラス (int の場合はInteger) に「詰める (box)」必要がある。
オブジェクトをコレクションから取り出すときは、格納した Integer を取得します。int が必要な場合は、intValue メソッドを使用して、Integer から「取り出す (unbox)」必要がある。
これを明示的に実装することは煩雑さの原因であるため、自動ボクシング、アンボクシングの機能がJavaには存在する。
プリミティブ型→ラッパークラスの自動変換を自動ボクシング
ラッパークラス→プリミティブ型の自動変換をアンボクシングと呼ぶ。

プリミティブ型 ラッパークラス
boolean Boolean
char Character
byte Byte
short Short
int Integer
long Long
float Float
double Double

不変(immutable)

作成後にその状態を変えることのできないオブジェクトのこと。

サンプルコード

このコードはこの箇所が実行されるたびに新しいStringインスタンスを生成してしまう。

例1
String s = new String("stringette");  //これをやってはいけない

これならOK

例2
String s = "stringette";  

その人がベビーブーム世代かどうかを判定するisBabyBoomerメソッドをもったPersonクラスを例に考えてみる。
↓の書き方だと、isBabyBoomerメソッドが呼ばれるたびに、不必要に新たなCalenderインスタンス、TimeZoneインスタンス、boomStart、boomEndのDateインスタンスを生成している。これはやってはいけない。

例3
public class Person {
    private final Date birthDate;

    //他のフィールド、メソッド、コンストラクタは省略

    //これをやってはいけない!
    public boolean isBabyBoomer() {
        //コストの高いオブジェクトの不必要な生成
        Calender gmtCal = 
            Calender.getInstance(TimeZone.getTimeZone("GMT"));
        gmtCal.set(1946, Calender.JANUARY, 1, 0, 0, 0);
        Date boomStart = gmtCal.getTime();
        gmtCal.set(1965, Calender.JANUARY, 1, 0, 0, 0);
        Date boomEnd = gmtCal.getTime();
        return birthDate.compareTo(boomStart) >= 0 &&
               birthDate.compareTo(boomEnd)   <  0 ;     
    }
}

例3の悪いところを改善したのが例4。
クラスが初期化された時点で1度だけCalenderインスタンス、TimeZoneインスタンス、BoomStart、BoomEndのDateインスタンスを生成している。
これによってisBabyBoomerメソッドが頻繁に呼ばれるのであればかなりのパフォーマンス向上になります。

例4
public class Person {
    private final Date birthDate;

    //他のフィールド、メソッド、コンストラクタは省略

    /**
     * ベビーブームの開始と終わりの日付。
     */
    private static final Date BOOM_START;
    private static final Date BOOM_END;

    static {
        Calender gmtCal =
            Calender.getInstance(TimeZone.getTimeZone("GMT");
        gmtCal.set(1946, Calender.JANUARY, 1, 0, 0, 0);
        BoomStart = gmtCal.getTime();
        gmtCal.set(1965, Calender.JANUARY, 1, 0, 0, 0);
        BoomEnd = gmtCal.getTime();
    }

    public boolean osBabyBoomer() {
        return birthDate.compareTo(BoomStart) >= 0 &&
               birthDate.compareTo(BoomEnd)   <  0 ;    
    }
}

例5は1文字の誤字のせいでかなり遅いです。
変数sumがlongではなくLongで宣言されています。
そのせいでプログラムは約2^31個の不必要なLongインスタンスを生成する。
(long i をLong sum に加算するごとに1つのインスタンスが増える)

教訓:ボクシングされた基本データ型よりも基本データ型を選び、意図しない自動ボクシングに注意すること

例5
//恐ろしく遅いプログラム! オブジェクト生成を指摘できますか?
public static void main(String[] args) {
    Long sum = OL;
    for (long i = 0; i <= Integer.MAX_VALUE; i++) {
        sum += i;
    }
    System.out.println(sum);
}

続く

【Effective Javaを読む】 第2章 項目6 『廃れたオブジェクト参照を取り除く』
https://qiita.com/Natsukii/items/5f2edd6a9bcdb94b03e5

0
1
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
1