[解決済み】Javaの「ダブルブレース初期化」の効率化?
質問
で Java の隠れた機能 を挙げています。 ダブルブレースの初期化 を使用すると 非常に 魅力的な構文です。
Set<String> flavors = new HashSet<String>() {{
add("vanilla");
add("strawberry");
add("chocolate");
add("butter pecan");
}};
このイディオムは、インスタンス・イニシャライザだけを持つ無名の内部クラスを作成し、そのクラスは "含むスコープで任意の [...] メソッドを使用できます" 。
本題です。これは 非効率 という感じでしょうか? 一回限りの初期化に限定すべきなのでしょうか? (もちろん、見せびらかしも!)。
2つ目の質問です。新しいHashSetは、インスタンスのイニシャライザーで使用される"this"でなければなりません...誰かこのメカニズムについて教えてください。
3つ目の質問です。このイディオムも 不明瞭 プロダクションコードで使うには?
概要 とても素晴らしい回答です、皆さんありがとうございます。質問(3)では、構文が明確であるべきだと感じているようです(ただし、特にコードが馴染みのない開発者に渡る場合は、時折コメントすることをお勧めします)。
質問(1)について、生成されたコードは素早く実行されるはずです。余分な .class ファイルは jar ファイルの散乱の原因となり、プログラムの起動を若干遅くします (測定してくれた @coobird に感謝します)。ガベージコレクションが影響を受ける可能性があると @Thilo が指摘しており、余分にロードされたクラスのメモリコストが要因になる場合もあります。
質問(2)は、私にとって最も興味深いものになりました。回答を理解すると、DBIで起こっていることは、匿名の内部クラスがnew演算子によって構築されるオブジェクトのクラスを拡張し、したがって構築されるインスタンスを参照する"this"値を持っているということです。非常に巧妙です。
全体として、DBIは知的好奇心の塊のようなものだと思います。 Coobirdや他の人は、Arrays.asList、varargsメソッド、Google Collections、そして提案されているJava 7 Collection literalsで同じ効果を得られると指摘しています。 Scala、JRuby、Groovyのような新しいJVM言語も、リスト構築のための簡潔な記法を提供し、Javaとうまく相互運用できます。 DBIはクラスパスを乱し、クラスの読み込みを少し遅くし、コードを少しわかりにくくすることを考えると、おそらく敬遠されることでしょう。しかし、私は、SCJPを取得したばかりで、Javaのセマンティクスについての気さくな議論が好きな友人に、これを勧めるつもりです!;-) みなさん、ありがとうございます。
7/2017: ベアード は、良いまとめがあります。 の二重ブレース初期化について、アンチパターンと考えています。
12/2017: @Basil Bourqueは、新しいJava 9では、あなたが言うことができることに注意してください。
Set<String> flavors = Set.of("vanilla", "strawberry", "chocolate", "butter pecan");
確かにその通りですね。 もし、以前のバージョンにこだわっているのなら、次のものを見てください。 Google CollectionsのImmutableSet .
解決するには?
ここで、匿名インナークラスで調子に乗りすぎると、こんな問題が発生します。
2009/05/27 16:35 1,602 DemoApp2$1.class
2009/05/27 16:35 1,976 DemoApp2$10.class
2009/05/27 16:35 1,919 DemoApp2$11.class
2009/05/27 16:35 2,404 DemoApp2$12.class
2009/05/27 16:35 1,197 DemoApp2$13.class
/* snip */
2009/05/27 16:35 1,953 DemoApp2$30.class
2009/05/27 16:35 1,910 DemoApp2$31.class
2009/05/27 16:35 2,007 DemoApp2$32.class
2009/05/27 16:35 926 DemoApp2$33$1$1.class
2009/05/27 16:35 4,104 DemoApp2$33$1.class
2009/05/27 16:35 2,849 DemoApp2$33.class
2009/05/27 16:35 926 DemoApp2$34$1$1.class
2009/05/27 16:35 4,234 DemoApp2$34$1.class
2009/05/27 16:35 2,849 DemoApp2$34.class
/* snip */
2009/05/27 16:35 614 DemoApp2$40.class
2009/05/27 16:35 2,344 DemoApp2$5.class
2009/05/27 16:35 1,551 DemoApp2$6.class
2009/05/27 16:35 1,604 DemoApp2$7.class
2009/05/27 16:35 1,809 DemoApp2$8.class
2009/05/27 16:35 2,022 DemoApp2$9.class
これらはすべて、シンプルなアプリケーションを作っているときに生成されたクラスで、大量の無名インナークラスを使っています -- 各クラスは別々の
class
ファイルを作成します。
このクラスはインスタンス初期化ブロックを持つ無名の内部クラスで、通常1つのオブジェクトを作成するために、初期化のたびに新しいクラスが作成されます。
Java 仮想マシンがこれらのクラスを使用する際にすべて読み込む必要があることを考慮すると、そのために
バイトコード検証
の処理などです。をすべて保存するために必要なディスク容量が増加することは言うまでもありません。
class
ファイルを作成します。
ダブルブレイスの初期化を利用すると、少しオーバーヘッドになるような気がするので、あまりやり過ぎない方がいいかもしれませんね。しかし、Eddieがコメントで述べているように、その影響を完全に確認することは不可能です。
参考までに、ダブルブレースの初期化は以下の通りです。
List<String> list = new ArrayList<String>() {{
add("Hello");
add("World!");
}};
Java の "hidden" 機能のように見えますが、単に書き換えただけです。
List<String> list = new ArrayList<String>() {
// Instance initialization block
{
add("Hello");
add("World!");
}
};
つまり、基本的には インスタンス初期化ブロック の一部であり 匿名内部クラス .
ジョシュア・ブロッホの コレクション リテラルの提案 に対して プロジェクトコイン は、そのような内容でした。
List<Integer> intList = [1, 2, 3, 4];
Set<String> strSet = {"Apple", "Banana", "Cactus"};
Map<String, Integer> truthMap = { "answer" : 42 };
悲しいかな、それは を実現できませんでした。 は、Java 7にも8にも搭載されず、無期限で棚上げされた。
実験
私が試した簡単な実験は次のとおりです。
ArrayList
という要素で
"Hello"
と
"World!"
を経由して追加されます。
add
という2つのメソッドを使用しています。
方法1:ダブルブレイスの初期化
List<String> l = new ArrayList<String>() {{
add("Hello");
add("World!");
}};
方法2:
ArrayList
と
add
List<String> l = new ArrayList<String>();
l.add("Hello");
l.add("World!");
2つの方法で1000回の初期化を行うために、Javaのソースファイルを書き出す簡単なプログラムを作成しました。
テスト1:
class Test1 {
public static void main(String[] s) {
long st = System.currentTimeMillis();
List<String> l0 = new ArrayList<String>() {{
add("Hello");
add("World!");
}};
List<String> l1 = new ArrayList<String>() {{
add("Hello");
add("World!");
}};
/* snip */
List<String> l999 = new ArrayList<String>() {{
add("Hello");
add("World!");
}};
System.out.println(System.currentTimeMillis() - st);
}
}
テスト2
class Test2 {
public static void main(String[] s) {
long st = System.currentTimeMillis();
List<String> l0 = new ArrayList<String>();
l0.add("Hello");
l0.add("World!");
List<String> l1 = new ArrayList<String>();
l1.add("Hello");
l1.add("World!");
/* snip */
List<String> l999 = new ArrayList<String>();
l999.add("Hello");
l999.add("World!");
System.out.println(System.currentTimeMillis() - st);
}
}
を初期化するまでの経過時間は、1000秒に1回であることに注意してください。
ArrayList
を拡張する1000個の匿名インナークラスと
ArrayList
を使用してチェックします。
System.currentTimeMillis
そのため、タイマーの分解能はあまり高くありません。私のWindowsシステムでは、解像度は15-16ミリ秒程度です。
2つのテストを10回実行した結果は、以下の通りです。
Test1 Times (ms) Test2 Times (ms)
---------------- ----------------
187 0
203 0
203 0
188 0
188 0
187 0
203 0
188 0
188 0
203 0
見ての通り、ダブルブレースの初期化の実行時間が約190msと顕著に現れています。
一方
ArrayList
初期化実行時間は0msと出ました。もちろん、タイマーの分解能を考慮する必要がありますが、15ms以下であることは間違いないと思われます。
ということで、2つの方法の実行時間には顕著な差があるようです。確かに、2つの初期化メソッドにはオーバーヘッドがあるように見えます。
そして、そう、1000個の
.class
をコンパイルして生成されたファイルです。
Test1
ダブルブレース初期化テストプログラム
関連
-
[解決済み] HTTP ステータス 500 - サーブレットクラス pkg.coreServlet のインスタンス化に失敗しました。
-
[解決済み] Javaでdoubleをfloatに変換する
-
[解決済み] JavaでInputStreamを読み込んでStringに変換するにはどうすればよいですか?
-
[解決済み] JavaでNullPointerExceptionを回避する方法
-
[解決済み] JavaにおけるHashMapとHashtableの違いは何ですか?
-
[解決済み] Java Mapの各エントリを効率的に反復処理するには?
-
[解決済み] Javaでメモリーリークを発生させるにはどうしたらいいですか?
-
[解決済み] JavaでArrayListではなくLinkedListを使用するのはいつですか?
-
[解決済み] JavaでStringをintに変換するにはどうしたらいいですか?
-
[解決済み] ArrayListの初期化を1行で行う。
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み] JVMフラグCMSClassUnloadingEnabledは、実際に何をするのですか?
-
[解決済み] HashMapのtoString関数はなぜ異なる順序で自分自身を印刷するのですか?
-
[解決済み] このフォーマット(Tue Jul 13 00:00:00 CEST 2010)の日付をJavaの日付に変換する方法(文字列はalfrescoのプロパティに由来しています)
-
[解決済み] パラメータ[変数]の不正な修飾子;finalのみが許可される[closed]。
-
[解決済み] HTTP ステータス 500 - サーブレットクラス pkg.coreServlet のインスタンス化に失敗しました。
-
[解決済み] Maven: assembly-pluginが全く実行されない
-
[解決済み] .lengthが解決できない、またはフィールドでない
-
[解決済み] ArrayListの初期化を1行で行う。
-
[解決済み] java.lang.OutOfMemoryError "に対処する。PermGen space "エラーに対処する
-
[解決済み】静的なMapを初期化する方法は?