3.2 装箱流
问题
用户希望利用基本类型流(primitive stream)创建集合。
方案
既可以使用 java.util.stream.IntStream 接口定义的 boxed 方法来包装元素,也可以使用合适的包装器类(wrapper class)来映射值,还可以使用 collect 方法的三参数形式。
讨论
在处理对象流(object stream)时,可以通过 Collectors 类提供的某种静态方法将流转换为集合。例如,对于一个给定的字符串流,例 3-8 显示了如何创建 List。
例 3-8 将字符串流转换为列表
List<String> strings = Stream.of("this", "is", "a", "list", "of", "strings").collect(Collectors.toList());
然而,同样的过程并不适合处理基本类型流,例 3-9 中的代码无法编译。
例 3-9 将
int流转换为Integer列表(无法编译)
IntStream.of(3, 1, 4, 1, 5, 9).collect(Collectors.toList()); // 无法编译
有三种替代方案可以解决这个问题。第一种方案是利用 boxed 方法,将 IntStream 转换为 Stream,如例 3-10 所示。
例 3-10 使用
boxed方法
List<Integer> ints = IntStream.of(3, 1, 4, 1, 5, 9).boxed() ➊.collect(Collectors.toList());
➊ 将 int 转换为 Integer
第二种方案是利用 mapToObj 方法,将基本类型流中的每个元素转换为包装类的一个实例,如例 3-11 所示。
例 3-11 使用
mapToObj方法
List<Integer> ints = IntStream.of(3, 1, 4, 1, 5, 9).mapToObj(Integer::valueOf).collect(Collectors.toList())
mapToInt、mapToLong 和 mapToDouble 方法将对象流解析为相关的基本类型流,与之类似,IntStream、LongStream 与 DoubleStream 中的 mapToObj 方法将基本类型流转换为相关包装类的实例。在本例中,mapToObj 方法的参数为 Integer 类定义的静态方法 valueOf。
出于性能方面的考虑,构造函数
Integer(int val)已被 JDK 9 弃用,建议改用Integer.valueOf(int)。
第三种方案是采用 collect 方法的三参数形式,其签名为:
<R> R collect(Supplier<R> supplier,ObjIntConsumer<R> accumulator,BiConsumer<R,R> combiner)
collect 方法的用法如例 3-12 所示。
例 3-12 使用
collect方法的三参数形式
- List<Integer> ints = IntStream.of(3, 1, 4, 1, 5, 9)
- .collect(ArrayList<Integer>::new, ArrayList::add, ArrayList::addAll);
如例所示,Supplier 是 ArrayList 的构造函数。累加器(accumulator)为 add 方法,表示如何为列表添加单个元素。仅在并行操作中使用的组合器(combiner)是 addAll 方法,它能将两个列表合二为一。尽管 collect 的三参数形式并不常见,但理解其用法对开发很有好处。
以上三种方案均无不妥,采用哪种方案取决于开发人员的编程风格。此外,如果希望将流转换为数组而非列表,那么采用 toArray 方法也不错,如例 3-13 所示。
例 3-13 将
IntStream转换为intArray
- int[] intArray = IntStream.of(3, 1, 4, 1, 5, 9).toArray();
本范例讨论的三种方案都是必不可少的,这是 Java 最初将基本数据类型与对象区别对待的结果,因泛型的引入而变得复杂。不过,一旦掌握要领,使用 boxed 或 mapToObj 方法还是很容易的。
另见
有关收集器的讨论请参见第 4 章,有关构造函数引用的讨论请参见范例 1.3。
出于性能方面的考虑,构造函数 