1.楔子
在开发中一个常见的操作就是新建Map或者List并进行初始化,但是在java的集合框架中并未提供好的初始化方式,只能先new,然后在一个个的put,但这样显得非常的low,如何才能优雅的初始化常见集合呢?
2. 一个看似很装逼的方式
2.1 代码实例
这种初始化方式使用了匿名内部类和非静态初始化块的奇技淫巧方式,详见代码如下:
package com.sino.daily.code_2019_3_30;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Created on 2019/3/30 9:48.
*
* @author caogu
*/
public class DoubleBraceInitialization {
// 新建map并初始化
private static Map<String, String> map1 = new HashMap<String, String>(){
// 匿名内部类 + 非静块初始化
{
this.put("1", "1");
this.put("2", "2");
}
};
private static List<String> list1 = new ArrayList<String>() {
{
this.add("1");
this.add("2");
}
};
public static void main(String[] args) {
System.out.println(map1);
System.out.println(list1);
}
}
输出结果为:
这种方式比起先new出对象,再一条条add,显得更加简洁和优雅。一开始没想通什么原理,后来查了一下才知道这种方法被称为双大括号初始化(double brace initialization)或者匿名内部类初始化法,实际上是一种取巧的方式。
2.2 代码解释
总的来说其原理是:外层“{}”创建了ArrayLIst的一个匿名子类,内层“{}”创建了一个对象构造块。
这里以ArrayList的例子解释,首先第一层花括号定义了一个继承于ArrayList的匿名内部类(Anonymous Inner Class)
:
//定义了一个继承于ArrayList的类,它没有名字
private static List<String> list1 = new ArrayList<String>() {
//在这里对这个类进行具体定义
};
第二层花括号实际上是这个匿名内部类实例初始化块(Instance Initializer Block)(或称为非静态初始化块)
:
private static List<String> list1 = new ArrayList<String>() {
//这里是实例初始化块,可以直接调用父类的非私有方法或访问非私有成员
{
this.add("1");
this.add("2");
}
};
我们通过new得到这个ArrayList的子类的实例并向上转型为ArrayList的引用:
ArrayList<String> list = new ArrayList<String>() {{}};
我们得到的实际上是一个ArrayList的子类的引用,虽然这个子类相比ArrayList并没有任何功能上的改变。
可以认为这是个本身装有数据的子类(因为它的数据来自于自身的初始化),而不是取得引用后再赋值。
结论:
这种方法一定程度上使代码更简洁,但同时可能降低可读性;还可能会出现一些潜在的未知问题,不建议使用!
3. 初始化集合的正确姿势
- 使用Arrays.asList (时间不是很高效,构造一个数组,转换为一个列表并使用该列表创建一个集合)
Set<String> h = new HashSet<String>(Arrays.asList("a","b"));
- 使用 Guava Sets(推荐使用)
Set<String> h = Sets.newHashSet("a","b","c");
- java8函数式编程
Set<String> set = Stream.of("a","b").collect(Collectors.toSet());
Set<String> set = Stream.of("a","b").collect(Collectors.toCollection(HashSet::new));