由于Java在语言集合框架中(collections,如list,map, set等)没有提供任何简单的语法结构,这使得在建立常量集合时的工作非常复杂。我们必须每次建立它:
1、定义空的集合变量
2、在这个组合类中逐一添加元素
3、将集合作为传递参数的方法
例如,把一个Set变量传递方法:
Setusers=newHashSet();users.add("Hollis");users.add("hollis");users.add("HollisChuang");users.add("hollis666");transferUsers(users);这种写法有点复杂。有简单的方法吗?
双括号语法的初始集合
事实上,有一较简单的方法,就是双括号语法(double-brace syntax)建立并初始化新的集合:
publicclassDoubleBraceTest{publicstaticvoidmain(String[]args){Setusers=newHashSet(){{add("Hollis");add("hollis");add("HollisChuang");add("hollis666");}};}}同样,创造和初始化也是如此。HashMap语法如下:
Map<String,String>users=newHashMap<>(){{put("Hollis","Hollis");put("hollis","hollis");put("HollisChuang","HollisChuang");}};不只是Set、Map,jdk这样可以创建和初始化中的集合类。
当我们使用这种双括号语法初始化集合类时,对Java编译文件时,可以发现使用的奇怪现象javac对DoubleBraceTest进行编译:
javacDoubleBraceTest.java我们会发现得到两个class文件:
DoubleBraceTest.classDoubleBraceTest$1.class有经验的朋友一看到这两份文件就可能知道匿名内部类必须用在里面。
是的,使用这种双括号初始化的效果是创建匿名内部类别。创建的类别有一个隐藏的this指针指向外部类。
不建议使用这种形式
首先,使用这种形式创建和初始化集合将导致创建许多内部类别。因为每次使用双括号初始化时,都会产生一个新的类别。
Maphollis=newHashMap(){{put("firstName","Hollis");put("lastName","Chuang");put("contacts",newHashMap(){{put("0",newHashMap(){{put("blogs","http://www.hollischuang.com");}});put("1",newHashMap(){{put("wechat","hollischuang");}});}});}};这将创造许多内部类别:
DoubleBraceTest$1$1$1.classDoubleBraceTest$1$1$2.classDoubleBraceTest$1$1.classDoubleBraceTest$1.classDoubleBraceTest.class创建这些内部类,需要被类加载器加载,这带来了一些额外的费用。
如果您使用上述代码在一种方法中创建和初始化map,并从方法返回map,然后,该方法的调用者可能会无意识地持有无法收集垃圾的资源。
publicMapgetMap(){Maphollis=newHashMap(){{put("firstName","Hollis");put("lastName","Chuang");put("contacts",newHashMap(){{put("0",newHashMap(){{put("blogs","http://www.hollischuang.com");}});put("1",newHashMap(){{put("wechat","hollischuang");}});}});}};returnhollis;}我们试着通过调用getMap通过双括号初始化获得这样一个map
publicclassDoubleBraceTest{publicstaticvoidmain(String[]args){DoubleBraceTestdoubleBraceTest=newDoubleBraceTest();Mapmap=doubleBraceTest.getMap();}}返回的Map现在将包含一对DoubleBraceTest的实例的引用。读者可以尝试这通过debug或以下方式确认事实。
Fieldfield=map.getClass().getDeclaredField("this$0");field.setAccessible(true);System.out.println(field.get(map).getClass());替代方案
很多人使用双括号初始化集合,主要是因为他更方便,可以在定义集合的同时初始化他。
但事实上,目前有很多方案可以做到这一点,不需要使用这种有风险的方案。
使用Arrays工具类
当我们想要初始化时,List可以借助Arrays类,Arrays中提供了asList可以将一个数组转换一个数组List:
List<String>list2=Arrays.asList("hollis","Hollis","HollisChuang");但需要注意的是,asList 只得到一个 Arrays 内部类是原数组的视图 List,因此,如果增删操作,会报错。
使用Stream
Stream是Java他可以筛选、排序、聚合传入流内的元素等中间操作(intermediate operate),最后,最终操作(terminal operation)以前处理的结果。
我们可以借助Stream初始化集合:初始化集合:java
List<String>list1=Stream.of("hollis","Hollis","HollisChuang").collect(Collectors.toList());使用第三方工具
许多第三方集合工具可以实现这一功能,例如Guava等:
ImmutableMap.of("k1","v1","k2","v2");ImmutableList.of("a","b","c","d");关于Guava我们将在第19章详细介绍定义的不可变集合
Java 9内置方法
其实在Java 9 中,在List、Map初始化方法已内置在集合类中,如List它包含12个重载of方法就是做这件事:
/***Returnsanunmodifiablelistcontainingzeroelements.**See<ahref="#unmodifiable">UnmodifiableLists</a>fordetails.**@param<E>the{@codeList}'selementtype*@returnanempty{@codeList}**@since9*/static<E>List<E>of(){returnImmutableCollections.emptyList();}static<E>List<E>of(Ee1){returnnewImmutableCollections.List12<>(e1);}static<E>List<E>of(E...elements){switch(elements.length){//implicitnullcheckofelementscase0:returnImmutableCollections.emptyList();case1:returnnewImmutableCollections.List12<>(elements[0]);case2:returnnewImmutableCollections.List12<>(elements[0],elements[1]);default:returnnewImmutableCollections.ListN<>(elements);}}