Java 9 – 快速创建不可变集合

平台之所以谓之平台,以其能建立一个生态,并与之外围达成共赢。霸道点的平台也会反噬外围生态,像微软集成浏览器,媒体播放器。还有即将的 iOS 12 要把应用商店多是收费的 AR 皮尺放到它自己系统中来,走别人的路,让别人无路可走。从此众泰皮尺部的唯一的生产工具就会是人手一部能安装 iOS 12 iPhone 了。

JDK 也不例外,Java 8 之前日期库的话 Joda-Time
是首要之选,Java 8 集成后应该是鲜有人问津。以往说到集合操作库,有两个选择,其一为 Apache Commons Collections
,二为 Google 的 Guava
,当然前者与后者竞争中也早已败下阵来,况且前者还受到 Java 8 的夹击。而本文要说的可以说是 Java 9 把 Guava 中创建不可变集合的方式据为已用了,直截了当的说,凡是 Java 9 后有创建不可变集合的需求,只要用三大接口 List
Set
Map
中的 of(...)
方法就对了。

Java 9 之前,当我们需要集合相关的操作,两个选择:

  1. Apache Commons Collections 的几个类 ListUtils
    , SetUtils
    , MapUtils
    , 和 CollectionsUtils
    。比如它们提供的以下几些个工具方法

    ListUtils.unmodifiableList<List<? extends E> list)   //创建不可变 List

    SetUtils.emptySet()  //不可变的空  Set

    SetUtils.unmodifiableSet(Set<? extends E> set)  //创建不可变 Set

    MapUtils.unmodifiableMap(Map<? extends K, ? extends V> map)  //创建不可变 Map

    CollectionUtils.unmodifiableCollection(Collection<? extends C> collection)  //创建不可变集合

  2. Guava 的几个类 ImmutableList
    , ImmutableSet
    , 和 ImmutableMap
    。而它们创建不可变集合的方式就是通过各自的 of(...)
     方法,以 ImmutableList
     为例(其余两个类也类似),它有

    of(): ImmutableList<E>  of(E element): ImmutableList<E>  of(E e1, E e2): ImmutableList<E>  of(E e1, E e2, E e3): ImmutableList<E>  ......  of(E e1, E e2, E e3, E e4, E e5, E e6, E e7, E e8, E e9, E e10, E e11, E e12, E... others): ImmutableList<E>
    

而我们今天要说的 Java 9 在接口 List, Set, Map 上增加的方法就是偷师于 Guava 的以上三个类,也是提供的一堆的 of(...)
方法来创建对应的不可变集合,不过还是略有增强。

既然是读的 《Java 9 Revealed》这本书的内容,也看下 Java 9 之前 JDK API Collections
可以怎么创建不可变集合,相关方法:

emptyList(): List<T>  emptySet(): Set<T>  emptyMap(): Map<K, V>
singletonList<T o): List<T>  singleton(T o): Set<T>  singletonMap(K key, V value): Map<K, V)
unmodifiableList(List<? extends T> list): List<T>  unmodifiableSet(Set<? extends T> s): Set<T>  unmodifiableMap(Map<? extends K, ? extends V> m): Map<K, V)

上面最后三个方法可以看到要创建不可变的 List, Set, 或 Map,需要对一个现有的集合进行包装,这种操作就有些不那么纯洁了,因为对原始集合的修改会影响到所谓的不可变集合。以 unmodifiableList(List<? extends T> list) 为例:

List<String> list = new ArrayList<>();
list.add("a");
List<String> immutableList = Collections.unmodifiableList(list);
//immutableList.add("hello"); //虽然不能这样做
list.add("b"); //但修改原始集合让人质疑 immutableList 的不可变性
System.out.println(immutableList);  //[a, b]

也就是 unmodifiableXxx(…) 产生的不可变集合实际上是有缺陷的。

而书中未提 Arrays.asList(T... a)
方法是由于它创建的是一个可变的 ArrayList<T>
实例,不在此讨论之列。

下面快速过一下 Java 9 的 List,Set,Map 的 of(...)
静态方法。先说一下那些 of(...)
及得到的结果的共同特点

  • 每个接口都提供了有限参数和不定参数的 of(...)
     方法,有限参数的 of(...)
     方法是为了性能考虑(如避免了参数装箱为数组)
  • 不同的 of(...)
     方法返回的内部实例类型也是不确定的,可以查看每一个 of(...)
     方法的返回类型
  • of(...)
     返回的实例都是可序列化的,所以只要保证其中的每一个元素(Map 则包括  key 和  value )是可序列化的,那么集体本身就可被序列化
  • 所有的 of(...)
     方法返回的都是真正的不可变集合,尝试对它们的任何修改都会抛出 UnsupportedOperationException 异常
  • 元素或元素的组成部分(Map 的 key 和 value) 都不允许 null 值的出现,否则抛出 NullPointerException 异常

List.of(…) 创建不可变的 List

接口 List
的静态 of(…) 方法有

  • static <E> List<E> of()   创建空列表,返回 ImmutableCollections.List0.instance()
  • static <E> List<E> of(E e1)    返回 new ImmutableCollections.List1<>(e1)
  • static <E> List<E> of(E e1, E e2) 返回 new ImmutalbeCollection.List2<>(e1, e2)
  • ……
  • static <E> List<E> of(E e1, E e2, E e3, E e4, E e5, E e6, E e7, E e8, E e9, E 10)
  • static <E> List<E> of(E… elements)

注意:不允许包含 null 元素,否则抛出 NullPointerException 异常

Set.of(…) 创建不可变的 Set

  • static <E> Set<E> of()   创建空 set
  • 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, E e4, E e5, E e6, E e7, E e8, E e9, E 10)
  • static <E> Set<E> of(E… elements)

注意:不允许包含 null 元素,否则抛出 NullPointerException 异常。并且不能有重复元素(调用方法时保证),否则抛出 IllegalArgumentException 异常。不像  HashSet 会帮我们去重。

Map.of(…) 创建不可变的  Map

  • static <K, V> Map<K, V> of()  创建空 Map
  • 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, 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) 

注间: of(...)
方法与  Guava 的 ImmutableMap 的用法是一样的,并且也是 key, value 都不允许有 null 值,否则抛出 NullPointerException 异常。 of(...)
方法的参数对偶出现,key 和 value 交替。对于不定元素个数无法用 of(...)
方法是因为 Java 只支持最后一个元素的可变,所以只能把 key/value 封装起来,引入上面的最后一个方法

static <K, V> Map<K, V> ofEntries(Map.Entry<? extends K, ? extends V>... entries)

再静态引入 Map.entry
方法后,我们使用 ofEntries(...)
方法的样式就是

import static java.util.Map.entry;
 
Map<Integer, String> numberToWord = Map.ofEntries(
    entry(1, "One"), entry(2, "Two"), entry(3, "Three"));

后话

Java 9 引入上述方法来创建不可变集合能够更有效使用内存,因为在集合的创建时元素的个数是确定的,不需要进行内部存储的动态伸展。

Java 9 使用 List,Set 和 Map 的 of(...)
方法来创建不可变已经是很大的进步了,当然不能与动态语言或 Scala 相比,看

Groovy 创建不可变  List 是

def list = ['Groovy', 'Java', 'Scala'].asImmutable()
def map = [key1: 'value1', key2: 'value2'].asImmutable()

Scala 借助于伴生类和 Apply 方法就更简洁了

val list = List("Groovy", "Java", "Scala")
val set = Set("A", "B", "C")
val map = Map("key1" -> "value1", "key2" -> "value2")

其实 JDK 是否包含流行的第三方组件库也是 Java 社区人民的呼声,不能怨 JDK 或 Oracle 的,总之方便的还是开发者

原文 

https://unmi.cc/java-9-quick-create-immutable-collections/

本站部分文章源于互联网,本着传播知识、有益学习和研究的目的进行的转载,为网友免费提供。如有著作权人或出版方提出异议,本站将立即删除。如果您对文章转载有任何疑问请告之我们,以便我们及时纠正。

PS:推荐一个微信公众号: askHarries 或者qq群:474807195,里面会分享一些资深架构师录制的视频录像:有Spring,MyBatis,Netty源码分析,高并发、高性能、分布式、微服务架构的原理,JVM性能优化这些成为架构师必备的知识体系。还能领取免费的学习资源,目前受益良多

转载请注明原文出处:Harries Blog™ » Java 9 – 快速创建不可变集合

分享到:更多 ()

评论 0

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址