Java9 集合工厂方法解析

 更新时间:2021年09月02日 15:14:14   作者:Lovnx  
这篇文章主要介绍了Java9 集合工厂方法,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教

使集合框架更便捷的工厂方法

JEP269中提议,为集合框架增添一些工厂方法,来使创建不可变集合类与含有少量元素的Map变得更加便捷。下文就为什么它们应运而生来展开详细的阐述。

集合框架增加工厂方法是必然的结果

Java饱受其语法臃肿的批评,比如,创建一个小而确定的集合类时(比如一个List),需要使用它的构造方法,然后将它的引用存放在局部变量中,通过引用来多次调用add()方法之后, 最后才来封装这个集合以获得不可变的视图。

早先的使用过程如下

List<String> list = new ArrayList<>();
list.add("a");
list.add("b");
list.add("c");
list = Collections.unmodifiableList(list);

上面这个语法如此臃肿的例子在先前的版本中并不能够简化,不可变的静态集合必须在静态初始块中来填充,而不是使用更加方便的字段表达式。但是,

也不得不提一下下面这些单语句表达式

List<String> list1 = 
   Collections.unmodifiableList(new ArrayList<>(Arrays.asList("a", "b", "c")));

List<String> list2 = 
   Collections.unmodifiableList(new ArrayList<String>() {{ add("a"); add("b"); add("c"); }});

List<String> list3 = 
   Collections.unmodifiableList(Stream.of("a", "b", "c").collect(toList()));
  • 第一种方式比较扯淡,你走遍千山,你跨过弱水,只为取一瓜瓢饮,是的,你没有看错,你费尽千辛万苦只为了生成一个包含a,b,c三个元素的List,并且你要构建一个ArrayList还要仰仗Arrays.asList(“a”, “b”, “c”)这个乌七八黑的方式,它不好用不说,关键是它在短短的生命周期之后还要被GC,过程还是不可见的。。。
  • 第二种好像看上去没那么扯淡,使用一个匿名内部类的实例初始化构造器来减少代码臃肿度,看上去很完美,但是可能会发生内存泄漏或者序列化的问题,因为它每次使用都会耗费额外的资源,还包含对封闭实例和任何捕获对象的隐藏引用。
  • 第三种方式是使用Java8的Streams API来完成的,虽然代码没那么臃肿,但是过程中也涉及到了不必要的对象创建。此外,Streams API不能用来构建Map, 除非值是经键计算而来或者stream的元素包含键值对。

为解决这些问题,JEP186提议了集合字面量的概念,集合字面量是一种句法表达式,采用一种类数组的方式,来创建List、Map或者其它的集合类

下面是其原始类型的简明表达方式

List<String> list = #[ "a", "b", "c" ];

没有任何新的语言特性,一切就像我们所思所想那样简明,但是这种集合字面量为什么没有被整合到Java9中去呢?取而代之的是,Java9采用了工厂方法来替代它,这其实是为了使语言改动尽量最小化,采用现有的方式,生产语法糖来达到这个目的的。

如此,集合工厂方法应运而生了。

一起来看看集合工厂方法

JEP 269的工厂方法受到类java.util.Collection和java.util.EnumSet类中的类似工厂方法的启发。 Collection提供用于创建空List,java.util.Set和Map的工厂方法,以及创建具有一个元素或键值对的单例List,Set和Map。 EnumSet提供了几个重载的of(…)工厂方法,它们采用固定或可变数量的参数,是为了更方便地创建指定元素的EnumSet。Java 9中的EnumSet模型的of()方法提供一致和通用的方式来创建包含任意类型对象的List,Set和Map。

以下工厂方法已添加到List接口中

static <E> List<E> of()
static <E> List<E> of(E e1)
static <E> List<E> of(E e1, E e2)
static <E> List<E> of(E e1, E e2, E e3)
static <E> List<E> of(E e1, E e2, E e3, E e4)
static <E> List<E> of(E e1, E e2, E e3, E e4, E e5)
static <E> List<E> of(E e1, E e2, E e3, E e4, E e5, E e6)
static <E> List<E> of(E e1, E e2, E e3, E e4, E e5, E e6, E e7)
static <E> List<E> of(E e1, E e2, E e3, E e4, E e5, E e6, E e7, E e8)
static <E> List<E> of(E e1, E e2, E e3, E e4, E e5, E e6, E e7, E e8, E e9) 
static <E> List<E> of(E e1, E e2, E e3, E e4, E e5, E e6, E e7, E e8, E e9, E e10)
static <E> List<E> of(E... elements)

以下工厂方法已添加到Set接口中

static <E> Set<E> of()
static <E> Set<E> of(E e1)
static <E> Set<E> of(E e1, E e2)
static <E> Set<E> of(E e1, E e2, E e3)
static <E> Set<E> of(E e1, E e2, E e3, E e4)
static <E> Set<E> of(E e1, E e2, E e3, E e4, E e5)
static <E> Set<E> of(E e1, E e2, E e3, E e4, E e5, E e6)
static <E> Set<E> of(E e1, E e2, E e3, E e4, E e5, E e6, E e7)
static <E> Set<E> of(E e1, E e2, E e3, E e4, E e5, E e6, E e7, E e8)
static <E> Set<E> of(E e1, E e2, E e3, E e4, E e5, E e6, E e7, E e8, E e9)
static <E> Set<E> of(E e1, E e2, E e3, E e4, E e5, E e6, E e7, E e8, E e9, E e10)
static <E> Set<E> of(E... elements)

在每个方法列表中, 第一个方法创建一个空的不可修改的集合。接下来的10个方法可创建1-10个元素的不可修改集合。尽管这些方法比较混乱,但它们避免了final类型的可变参方法产生的数组分配,初始化和垃圾回收开销,这种方法还支持任意大小的集合。

以下是List和Set的示例

import java.util.List;
import java.util.Set;
public class ColDemo
{
   public static void main(String[] args)
   {
      List<String> fruits = List.of("apple", "orange", "banana");
      for (String fruit: fruits)
         System.out.println(fruit);
      try
      {
         fruits.add("pear");
      }
      catch (UnsupportedOperationException uoe)
      {
         System.err.println("unable to modify fruits list");
      }

      Set<String> marbles = Set.of("aggie", "alley", "steely");
      for (String marble: marbles)
         System.out.println(marble);
      try
      {
         marbles.add("swirly");
      }
      catch (UnsupportedOperationException uoe)
      {
         System.err.println("unable to modify marbles set");
      }
   }
}

运行后输出:

apple
orange
banana
unable to modify fruits list
steely
alley
aggie
unable to modify marbles set

以下工厂方法则添加到Map接口中

static <K,V> Map<K,V> 
   of()
static <K,V> Map<K,V> 
   of(K k1, V v1)
static <K,V> Map<K,V> 
   of(K k1, V v1, K k2, V v2)
static <K,V> Map<K,V> 
   of(K k1, V v1, K k2, V v2, K k3, V v3)
static <K,V> Map<K,V> 
   of(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4)
static <K,V> Map<K,V> 
   of(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5    
static <K,V> Map<K,V> 
   of(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5, K k6, V v6)
static <K,V> Map<K,V> 
   of(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5, K k6, V v6, K k7, V v7    
static <K,V> Map<K,V> 
   of(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5, K k6, V v6, K k7, V v7, 
      K k8, V v8)
static <K,V> Map<K,V> 
   of(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5, K k6, V v6, K k7, V v7, 
      K k8, V v8, K k9, V v9)
static <K,V> Map<K,V> 
   of(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5, K k6, V v6, K k7, V v7, 
      K k8, V v8, K k9, V v9, K k10, V v10)
static <K,V> Map<K,V> 
   ofEntries(Map.Entry<? extends K,? extends V>... entries)

第一个方法创建了一个空的不可变的Map,接下来10个方法创建包含1-10和键值对的Map,尽管这些方法比较混乱,但它们避免了final类型的可变参方法产生的数组分配,初始化和垃圾回收开销,且支持任意大小的Map。

虽然Map的可变参数方法近似List和Set的,但是它的每个键值对必须被包装起来,下面这个方法可以方便地将包装键值对转换为Map标准键值对:

Map.Entry<K,V> entry(K k, V v)

下面是Map的ofEntries() 和entry()方法的示例

import java.util.Map;
import static java.util.Map.entry;
public class MapDemo
{
   public static void main(String[] args)
   {
      Map<String, String> capCities = 
         Map.ofEntries(entry("Manitoba", "Winnipeg"), 
                       entry("Alberta", "Edmonton"));
      capCities.forEach((k, v) -> 
                        System.out.printf("Key = %s, Value = %s%n", k, v));
      try
      {
         capCities.put("British Columbia", "Victoria");
      }
      catch (UnsupportedOperationException uoe)
      {
         System.err.println("unable to modify capCities map");
      }
   }
}

运行后输出:

Key = Alberta, Value = Edmonton
Key = Manitoba, Value = Winnipeg
unable to modify capCities map

注意,未来的JDK版本可能会让开发者指定值类型来减少包装键值对所带来的性能开销,从entry()方法可以看出,通过它返回一个新的实现自Map.Entry的具体引用类型,我想这是为了后面把潜在特性迁移到值类型中去设下的铺垫吧。

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • Java实现平铺列表(List)互转树形(Tree)结构

    Java实现平铺列表(List)互转树形(Tree)结构

    本文主要介绍了Java实现平铺列表(List)互转树形(Tree)结构,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2022-08-08
  • springboot 在linux后台运行的方法

    springboot 在linux后台运行的方法

    这篇文章主要介绍了springboot 在linux后台运行的方法,非常不错,具有一定的参考借鉴价值,需要的朋友可以参考下
    2018-06-06
  • Spring中的@RefreshScope注解作用

    Spring中的@RefreshScope注解作用

    这篇文章主要介绍了Spring中的@RefreshScope注解作用详解,@RefreshScope注解是Spring Cloud中的一个重要注解,用于实现动态刷新配置的功能,当我们在应用程序中使用@Value注解获取配置属性时,如果配置发生变化,需要重启应用程序才能生效,需要的朋友可以参考下
    2023-10-10
  • Java Math类的三个方法ceil,floor,round用法

    Java Math类的三个方法ceil,floor,round用法

    这篇文章主要介绍了Java Math类的三个方法ceil,floor,round用法,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-07-07
  • Java详解HashMap实现原理和源码分析

    Java详解HashMap实现原理和源码分析

    这篇文章主要介绍了Java关于HashMap的实现原理并进行源码分析,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2021-09-09
  • mybatis连接mysql的实现过程

    mybatis连接mysql的实现过程

    通过配置Maven的pom文件,可以简化MyBatis连接数据库的过程,免去手动下载和导入各种依赖包的麻烦,本文介绍了如何利用Maven导入MyBatis及其他相关依赖,如Junit、MySQL连接驱动、Druid连接池和Dbutil等,以简化数据库操作和测试
    2024-10-10
  • Java动态获取实现某个接口下所有的实现类对象集合

    Java动态获取实现某个接口下所有的实现类对象集合

    今天小编就为大家分享一篇关于Java动态获取实现某个接口下所有的实现类对象集合,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看吧
    2018-12-12
  • Spring用AspectJ开发AOP(基于Annotation)

    Spring用AspectJ开发AOP(基于Annotation)

    这篇文章主要介绍了Spring用AspectJ开发AOP(基于Annotation),文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2019-10-10
  • Maven  pom.xml与settings.xml详解

    Maven pom.xml与settings.xml详解

    这篇文章主要介绍了Maven pom.xml与settings.xml详解的相关资料,这里对pom.xml与setting.xml详细的研究说明,需要的朋友可以参考下
    2016-12-12
  • Java反射机制在Spring IOC中的应用详解

    Java反射机制在Spring IOC中的应用详解

    这篇文章主要介绍了Java反射机制在Spring IOC中的应用,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-09-09

最新评论