Java 流的创建,用户希望从数据源创建流,使用 java.util.stream.Stream 接口定义的各种静态工厂方法,以及 java.lang.Iterable 接口或 java.util.Arrays 类定义的 stream 方法。Java 8 引入的 Stream 接口定义了多种用于创建流的静态方法。具体而言,可以采用 Stream.of、Stream.iterate、Stream.generate 等静态方法创建流。
Java 流的创建 问题描述
用户希望从数据源创建流。
Java 流的创建 解决方案
使用 java.util.stream.Stream 接口定义的各种静态工厂方法,以及 java.lang.Iterable 接口或 java.util.Arrays 类定义的 stream 方法。
Java 流的创建 具体实例
Java 8 引入的 Stream 接口定义了多种用于创建流的静态方法。具体而言,可以采用 Stream.of、Stream.iterate、Stream.generate 等静态方法创建流。
Stream.of 方法传入元素的可变参数列表:
static <T> Stream<T> of(T... values)
在 Java 标准库中,of 方法的实现实际上被委托给 java.util.Arrays 类定义的 stream 方法,如例 3-1 所示。
例 3-1 Stream.of 方法的引用实现
@SafeVarargs public static<T> Stream<T> of(T... values) { return Arrays.stream(values); }
@SafeVarargs 注解属于 Java 泛型的一部分,它在使用数组作为参数时出现。因为用户有可能将一个类型化数组(typed array)赋给一个 Object 数组,导致添加的元素引发类型安全问题。换言之,@SafeVarargs 注解构成了开发人员对类型安全的承诺。详见泛型与 Java 8。
Stream.of 方法的简单应用如例 3-2 所示。
由于流在遇到终止表达式之前不会处理任何数据,本范例中的所有示例都会在末尾添加一个终止方法,如 collect 或 forEach。
例 3-2 利用 Stream.of 方法创建流
String names = Stream.of("Gomez", "Morticia", "Wednesday", "Pugsley") .collect(Collectors.joining(",")); System.out.println(names); // 打印Gomez,Morticia,Wednesday,Pugsley
Java API 还定义了 of 方法的重载形式,它传入单个元素 T t,返回只包含一个元素的单例顺序流(singleton sequential stream)。
Arrays.stream 方法的应用如例 3-3 所示。
例 3-3 利用 Arrays.stream 方法创建流
String[] munsters = { "Herman", "Lily", "Eddie", "Marilyn", "Grandpa" }; names = Arrays.stream(munsters) .collect(Collectors.joining(",")); System.out.println(names); // 打印Herman,Lily,Eddie,Marilyn,Grandpa
由于需要提前创建数组,上述方案略有不便,但足以满足可变参数列表的需要。Java API 定义了 Arrays.stream 方法的多种重载形式,用于处理 int、long 和 double 型数组,还定义了本例使用的泛型类型。
Stream 接口定义的另一种静态工厂方法是 iterate,其签名如下:
static <T> Stream<T> iterate(T seed, UnaryOperator<T> f)
根据 Javadoc 的描述,iterate 方法“返回一个无限顺序的有序流(infinite sequential ordered stream),它由迭代应用到初始元素种子的函数 f 产生”。回顾一下范例Function接口讨论的 UnaryOperator,它是一种函数式接口,其输入参数和输出类型相同。如果有办法根据当前值生成流的下一个值,iterate 方法将相当有用,如例 3-4 所示。
例 3-4 利用 Stream.iterate 方法创建流
List<BigDecimal> nums = Stream.iterate(BigDecimal.ONE, n -> n.add(BigDecimal.ONE) ) .limit(10) .collect(Collectors.toList()); System.out.println(nums); // 打印[1, 2, 3, 4, 5, 6, 7, 8, 9, 10] Stream.iterate(LocalDate.now(), ld -> ld.plusDays(1L)) .limit(10) .forEach(System.out::println) // 打印从当日开始之后的10天
第一段代码采用 BigDecimal 实例,从 1 开始递增。第二段代码采用 java.time 包新增的 LocalDate 类,从当日开始按天递增。由于生成的两个流都是无界的,需要通过中间操作 limit 加以限制。
Stream 接口还定义了静态工厂方法 generate,其签名为:
static <T> Stream<T> generate(Supplier<T> s)
generate 方法通过多次调用 Supplier 产生一个顺序的无序流(sequential, unordered stream)。在 Java 标准库中,Supplier 的一种简单应用是 Math.random 方法,它不传入参数而返回 double 型数据,如例 3-5 所示。
例 3-5 利用 Math.random 创建随机流(double 型)
long count = Stream.generate(Math::random) .limit(10) .forEach(System.out::println)
如果已有集合,可以利用 Collection 接口新增的默认方法 stream,如例 3-6 所示。1
1希望承认以下事实不会让我的声誉毁于一旦:我随口就能叫出《脱线家族》中六个孩子的姓名。真的,我也没想到自己的记忆力会这么好。
例 3-6 从集合创建流
List<String> bradyBunch = Arrays.asList("Greg", "Marcia", "Peter", "Jan", "Bobby", "Cindy"); names = bradyBunch.stream() .collect(Collectors.joining(",")); System.out.println(names); // 打印Greg,Marcia,Peter,Jan,Bobby,Cindy
Stream 接口定义了三种专门用于处理基本数据类型的子接口,它们是 IntStream、LongStream 和 DoubleStream。IntStream 和 LongStream 还包括另外两种创建流所用的工厂方法 range 和 rangeClosed,二者的方法签名如下:
static IntStream range(int startInclusive, int endExclusive) static IntStream rangeClosed(int startInclusive, int endInclusive) static LongStream range(long startInclusive, long endExclusive) static LongStream rangeClosed(long startInclusive, long endInclusive)
注意这几个语句中的参数有所不同:rangeClosed 包含终值(end value),而 range 不包含终值。两种方法都返回一个顺序的有序流,从第一个参数开始逐一递增。例 3-7 展示了 range 和 rangeClosed 方法的应用。
例 3-7 range 和 rangeClosed 方法
List<Integer> ints = IntStream.range(10, 15) .boxed() ➊ .collect(Collectors.toList()); System.out.println(ints); // 打印[10, 11, 12, 13, 14] List<Long> longs = LongStream.rangeClosed(10, 15) .boxed() ➊ .collect(Collectors.toList()); System.out.println(longs); // 打印[10, 11, 12, 13, 14, 15]
➊ Collectors 需要将基本数据类型转换为 List<T>
本例唯一的奇怪之处在于使用了 boxed 方法将 int 值转换为 Integer 实例。有关 boxed 方法的详细讨论请参见范例装箱流。
创建流所用的方法总结如下。
- Stream.of(T... values) 和 Stream.of(T t)
- Arrays.stream(T[] array) 以及用于处理 int[]、double[] 与 long[] 型数组的重载形式
- Stream.iterate(T seed, UnaryOperator<T> f)
- Stream.generate(Supplier<T> s)
- Collection.stream()
- 使用 range 和 rangeClosed 方法:
- IntStream.range(int startInclusive, int endExclusive)
- IntStream.rangeClosed(int startInclusive, int endInclusive)
- LongStream.range(long startInclusive, long endExclusive)
- LongStream.rangeClosed(long startInclusive, long endInclusive)