3.2 装箱流

问题

用户希望利用基本类型流(primitive stream)创建集合。

方案

既可以使用 java.util.stream.IntStream 接口定义的 boxed 方法来包装元素,也可以使用合适的包装器类(wrapper class)来映射值,还可以使用 collect 方法的三参数形式。

讨论

在处理对象流(object stream)时,可以通过 Collectors 类提供的某种静态方法将流转换为集合。例如,对于一个给定的字符串流,例 3-8 显示了如何创建 List

例 3-8 将字符串流转换为列表

  1. List<String> strings = Stream.of("this", "is", "a", "list", "of", "strings")
  2. .collect(Collectors.toList());

然而,同样的过程并不适合处理基本类型流,例 3-9 中的代码无法编译。

例 3-9 将 int 流转换为 Integer 列表(无法编译)

  1. IntStream.of(3, 1, 4, 1, 5, 9)
  2. .collect(Collectors.toList()); // 无法编译

有三种替代方案可以解决这个问题。第一种方案是利用 boxed 方法,将 IntStream 转换为 Stream,如例 3-10 所示。

例 3-10 使用 boxed 方法

  1. List<Integer> ints = IntStream.of(3, 1, 4, 1, 5, 9)
  2. .boxed()
  3. .collect(Collectors.toList());

➊ 将 int 转换为 Integer

第二种方案是利用 mapToObj 方法,将基本类型流中的每个元素转换为包装类的一个实例,如例 3-11 所示。

例 3-11 使用 mapToObj 方法

  1. List<Integer> ints = IntStream.of(3, 1, 4, 1, 5, 9)
  2. .mapToObj(Integer::valueOf)
  3. .collect(Collectors.toList())

mapToIntmapToLongmapToDouble 方法将对象流解析为相关的基本类型流,与之类似,IntStreamLongStreamDoubleStream 中的 mapToObj 方法将基本类型流转换为相关包装类的实例。在本例中,mapToObj 方法的参数为 Integer 类定义的静态方法 valueOf

3.2 装箱流 - 图1 出于性能方面的考虑,构造函数 Integer(int val) 已被 JDK 9 弃用,建议改用 Integer.valueOf(int)

第三种方案是采用 collect 方法的三参数形式,其签名为:

  1. <R> R collect(Supplier<R> supplier,
  2. ObjIntConsumer<R> accumulator,
  3. BiConsumer<R,R> combiner)

collect 方法的用法如例 3-12 所示。

例 3-12 使用 collect 方法的三参数形式

  1. List<Integer> ints = IntStream.of(3, 1, 4, 1, 5, 9)
  2. .collect(ArrayList<Integer>::new, ArrayList::add, ArrayList::addAll);

如例所示,SupplierArrayList 的构造函数。累加器(accumulator)为 add 方法,表示如何为列表添加单个元素。仅在并行操作中使用的组合器(combiner)是 addAll 方法,它能将两个列表合二为一。尽管 collect 的三参数形式并不常见,但理解其用法对开发很有好处。

以上三种方案均无不妥,采用哪种方案取决于开发人员的编程风格。此外,如果希望将流转换为数组而非列表,那么采用 toArray 方法也不错,如例 3-13 所示。

例 3-13 将 IntStream 转换为 intArray

  1. int[] intArray = IntStream.of(3, 1, 4, 1, 5, 9).toArray();

本范例讨论的三种方案都是必不可少的,这是 Java 最初将基本数据类型与对象区别对待的结果,因泛型的引入而变得复杂。不过,一旦掌握要领,使用 boxedmapToObj 方法还是很容易的。

另见

有关收集器的讨论请参见第 4 章,有关构造函数引用的讨论请参见范例 1.3。