Вот проблема, когда я слишком увлекаюсь анонимными внутренними классами:
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
.
«Двойная инициализация», как уже упоминалось, является анонимным внутренним классом с блоком инициализации экземпляра, что означает, что для каждой «инициализации» создается новый класс, обычно с целью создания единого объекта.
Учитывая, что виртуальной машине Java нужно будет читать все эти классы при их использовании, это может привести к некоторому времени в процессе проверки байт-кода и так далее. Не говоря уже об увеличении необходимого дискового пространства для хранения всех этих 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!");
}
};
Таким образом, это в основном блок инициализации экземпляра , который является частью анонимного внутреннего класса .
Предложение Джошуа Блоха Collection Literals для Project Coin было следующим:
List<Integer> intList = [1, 2, 3, 4];
Set<String> strSet = {"Apple", "Banana", "Cactus"};
Map<String, Integer> truthMap = { "answer" : 42 };
К сожалению, он не пробился ни в Java 7, ни в 8 и был отложен на неопределенный срок.
Эксперимент
Вот простой эксперимент, который я протестировал - проведите 1000 ArrayList
с элементами "Hello"
и "World!"
, добавленными к ним с помощью метода add
, используя два метода:
Метод 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!");
Я создал простую программу для записи исходного файла Java для выполнения 1000 инициализаций, используя два метода:
Тест 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 миллисекунд.
Результаты 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
оказалось равным 0 мс. Конечно, следует учитывать разрешение таймера, но оно может быть меньше 15 мс.
Так что, кажется, есть заметная разница во времени выполнения двух методов. Похоже, что в двух методах инициализации действительно есть издержки.
И да, было создано 1000 .class
файлов, сгенерированных с помощью программы проверки инициализации с двойной скобкой Test1
.