2010年4月17日土曜日

Effective javaまとめ4 重複したオブジェクトを生成するのを避ける1

過去のブログを読み返してましたが、日本語が崩壊しているところが多々ありますね・・。
直そうかと思いましたが、めんどーくさいので放置します。ごめんなさい。

---------------------------------------
項目4 重複したオブジェクトを生成するのを避ける

機能的に同じオブジェクトが必要となる都度、新たに生成する代わりに、1つのオブジェクトを再利用することが、大抵の場合適切です。オブジェクトが不変(状態を変更することができない)であれば常に再利用できます。

不変オブジェクトの代表とも言えるStringを利用した以下の例を見てみます。

---------------------------------------
【パターン1】

String s = new String("kows");

---------------------------------------

これは、実行される度に新たなオブジェクトが生成されます。
これを、以下のように変更します。

---------------------------------------
【パターン2】

String s = "kows";

---------------------------------------

これは、実行される度に新たなオブジェクトを生成せず、同一のStringオブジェクトを使いまわします。どちらのコードが効率が良いでしょうか。

---------------------------------------
【パターン1を100万回繰り返す:重複オブジェクトを生成する】

public static void main(String[] args) {
long start = System.currentTimeMillis();
for(int i = 0; i < 1000000; i++) {
String s = new String("mackagy");
}
long end = System.currentTimeMillis();
System.out.println(end - start);
}

結果:409

【パターン2を100万回繰り返す:オブジェクトを使いまわす】

public static void main(String[] args) {
long start = System.currentTimeMillis();
for(int i = 0; i < 1000000; i++) {
String s = "mackagy";
}
long end = System.currentTimeMillis();
System.out.println(end - start);
}

結果:40

---------------------------------------

オブジェクトを使いまわす方が効率が良いみたいです。

※超余談ですが、パターン1を String s = new String() で実行すると、一番遅いです。
String()は、内部で更にchar配列をnewするためです。

不変クラスにはだいたいstaticファクトリーメソッドが提供されています。自分でクラスを設計する場合も提供するべきです。コンストラクタは呼び出される度に新たなオブジェクトを生成しますが、staticファクトリーメソッドは新たなオブジェクトを生成しません。

不変オブジェクトを再利用する以外にも、決して修正されることが分かっている可変オブジェクトを再利用することもできます。

以下の例を見てください
----------------------------------------
【パターン1】

public class Person {
private final Date birthDate;

public Person (Date birthDate) {
this.birthDate = birthDate;
}

public boolean bornAfter2000() {
Calendar base = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
base.set(2000, Calendar.JANUARY, 1, 0, 0, 0);
Date baseDate = base.getTime();
return baseDate.compareTo(birthDate) >= 0;
}

}
----------------------------------------
bornAfter2000メソッド(役割がかなり変ですがあんまり気にしないでください)は呼び出されるごとに、Calendar、Dateオブジェクトを生成します。しかし、これは毎回生成しなくても良いはずです。(必ず2000年1月1日固定となっているため)

これは、以下のように変更することができます。
----------------------------------------
【パターン2】

public class Person {
private final Date birthDate;
private static Date baseDate;

static {
Calendar base = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
base.set(2000, Calendar.JANUARY, 1, 0, 0, 0);
baseDate = base.getTime();
}

public Person (Date birthDate) {
this.birthDate = birthDate;
}

public boolean bornAfter2000() {
return birthDate.compareTo(baseDate) >= 0;
}
}
----------------------------------------
staticイニシャライザは、クラスがロードされた時点で一度だけ実行されるため、不必要にCalendar, Dateオブジェクトが生成されなくなります。

それぞれ100万回実行すると、パターン1は2912ミリ秒かかりますが、パターン2はたった19ミリ秒です。かなり効果があるといえると思います。

ただし、パターン1ではbornAfter2000が呼ばれない限りCalendar,Dateのインスタンスが生成されることはありませんでしたが、パターン2では必ずbornAfter2000を呼び出さなくても、必ずインスタンスが生成されることになります。このへんはちょっと注意が必要です。

---> 続く

0 件のコメント:

コメントを投稿