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)