匿名の内部クラスに夢中になっているときの問題は次のとおりです。
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 Virtual Machineがこれらのクラスを使用する場合、それらすべてのクラスを読み取る必要があることを考えると、バイトコード検証プロセスなどに時間がかかる可能性があります。これらすべてのclass
ファイルを保存するために必要なディスク容量の増加は言うまでもありません。
ダブルブレースの初期化を利用するときにオーバーヘッドが少しあるように思われるので、多すぎてやり過ぎることはおそらくあまり良い考えではありません。しかし、エディがコメントで述べたように、影響を完全に確信することは不可能です。
参考までに、二重ブレースの初期化は次のとおりです。
List<String> list = new ArrayList<String>() {{
add("Hello");
add("World!");
}};
これは、Javaの「隠された」機能のように見えますが、これは単に次のように書き直したものです。
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にもJava 8にも進まず、無期限に棚上げされました。
実験
これが私がテストした簡単な実験です- ArrayList
要素で1000 秒を作成し、2つのメソッドを使用して、メソッドを介して要素"Hello"
に"World!"
追加しますadd
。
方法1:二重ブレースの初期化
List<String> l = new ArrayList<String>() {{
add("Hello");
add("World!");
}};
方法2:ArrayList
andをインスタンス化する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 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
見てわかるように、二重ブレースの初期化には、約190ミリ秒という顕著な実行時間があります。
一方、ArrayList
初期化実行時間は0msとなった。もちろん、タイマーの解像度を考慮する必要がありますが、15ミリ秒未満になる可能性があります。
したがって、2つのメソッドの実行時間には顕著な違いがあるようです。2つの初期化方法には確かにオーバーヘッドがあるようです。
そして、はい、二重ブレース初期化テストプログラムを.class
コンパイルすることによって生成された1000個のファイルがありましたTest1
。
flavors
することですがHashSet
、残念ながらそれは匿名のサブクラスです。