什么是Java雙大括號初始化?
通常情況下,初始化Java集合并向其中添加幾個元素的步驟如下:
Set<Integer> set = new HashSet<>();
set.add(1);
set.add(2);
set.add(3);
或者我們可以在靜態初始化塊中向作為靜態變量的集合添加元素:
private static final Set<Integer> set = new HashSet<>();
static {
set.add(1);
set.add(2);
set.add(3);
}
從語法上來看,這樣的初始化方法雖然格式清晰明了,但語法上略顯冗余。事實上,Java允許一種精簡的雙大括號初始化方法:
Set<Integer> set = new HashSet<Integer>() {{
add(1);
add(2);
add(3);
}};
或是在接收集合作為輸入的函數中直接初始化:
someFunction(new HashSet<Integer>() {{
add(1);
add(2);
add(3);
}}
);
語法解讀
事實上,如下雙大括號初始化Set
的代碼
Set<Integer> set = new HashSet<Integer>() {{
add(1);
add(2);
add(3);
}};
是以下這段代碼的改寫:
Set<Integer> set = new HashSet<Integer>() {
{
add(1);
add(2);
add(3);
}
};
改寫之后的代碼就非常容易理解了。顯然這是在HashSet的構造器中寫了一個匿名內部類,這個匿名內部類含有一個實例初始化塊,初始化塊的內容是三個add()
函數,向被初始化的this
指向的HashSet
中添加了三個元素。
效率問題和產生的.class
文件結構
利用雙大括號初始化集合從效率上來說可能不如標準的集合初始化步驟。原因在于使用雙大括號初始化會導致內部類文件的產生,而這個過程就會影響代碼的執行效率。
例如如下代碼:
// Double brace initialization
class Test1 {
public static void main(String[] args) {
long st = System.currentTimeMillis();
Set<Integer> set0 = new HashSet<Integer>() {{
add(1);
add(2);
}};
Set<Integer> set1 = new HashSet<Integer>() {{
add(1);
add(2);
}};
/* snip */
Set<Integer> set999 = new HashSet<Integer>() {{
add(1);
add(2);
}};
System.out.println(System.currentTimeMillis() - st);
}
}
// Normal initialization
class Test2 {
public static void main(String[] s) {
long st = System.currentTimeMillis();
Set<Integer> set0 = new HashSet<Integer>();
set0.add(1);
set0.add(2);
Set<Integer> set1 = new HashSet<Integer>();
set1.add(1);
set1.add(2);
/* snip */
Set<Integer> set999 = new HashSet<Integer>();
set999.add(1);
set999.add(2);
System.out.println(System.currentTimeMillis() - st);
}
}
用javac
命令compile第一段代碼會產生1000個.class
文件,如下所示:
Test1$1.class
Test1$2.class
...
Test1$1000.class
同時從程序輸出的運行時間來看, 雙大括號初始化也顯著慢于普通初始化的代碼。差值大約在100ms左右。