Java 流的创建

Java 流的创建,用户希望从数据源创建流,使用 java.util.stream.Stream 接口定义的各种静态工厂方法,以及 java.lang.Iterable 接口或 java.util.Arrays 类定义的 stream 方法。Java 8 引入的 Stream 接口定义了多种用于创建流的静态方法。具体而言,可以采用 Stream.ofStream.iterateStream.generate 等静态方法创建流。

Java 流的创建 问题描述

用户希望从数据源创建流。

Java 流的创建 解决方案

使用 java.util.stream.Stream 接口定义的各种静态工厂方法,以及 java.lang.Iterable 接口或 java.util.Arrays 类定义的 stream 方法。

Java 流的创建 具体实例

Java 8 引入的 Stream 接口定义了多种用于创建流的静态方法。具体而言,可以采用 Stream.ofStream.iterateStream.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 所示。
由于流在遇到终止表达式之前不会处理任何数据,本范例中的所有示例都会在末尾添加一个终止方法,如 collectforEach
 

例 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 方法的多种重载形式,用于处理 intlongdouble 型数组,还定义了本例使用的泛型类型。
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 接口定义了三种专门用于处理基本数据类型的子接口,它们是 IntStreamLongStreamDoubleStreamIntStreamLongStream 还包括另外两种创建流所用的工厂方法 rangerangeClosed,二者的方法签名如下:

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 展示了 rangerangeClosed 方法的应用。

例 3-7 rangerangeClosed 方法

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()
  • 使用 rangerangeClosed 方法:
    • IntStream.range(int startInclusive, int endExclusive)
    • IntStream.rangeClosed(int startInclusive, int endInclusive)
    • LongStream.range(long startInclusive, long endExclusive)
    • LongStream.rangeClosed(long startInclusive, long endInclusive)

Python教程

Java教程

Web教程

数据库教程

图形图像教程

大数据教程

开发工具教程

计算机教程