Streams允许我们以声明的方式对数据集合进行计算。在一种声明式编程方式中,您不指定如何做,而是指定要做什么。您可以创建流管道来执行计算。流管道包括:
- 消息来源
- 零个或多个中间操作
- 终端操作
Java streams流是懒惰的。因此,仅当终端操作启动时才执行中间操作。
Java streams pipeline 流管道
让我们考虑一个从图书目录中查找所有java图书作者的例子。使用streams流声明性构造,您可以将代码编写为:
List<Book> books =
Catalog.books();
List<String> javaAuthors =
books.stream()
.filter(book -> book.getCategory().equals(JAVA))
.map(Book::getAuthor)
.collect(Collectors.toList());
让我们试着在“流管道由一个源、零个或多个中间操作和一个终端操作组成”的上下文中理解上述流管道
Stream源
在Java中,Stream的源可以是数组、集合、生成器函数、I/O通道、值范围等。在上面的示例中,Stream流是通过调用集合类的stream()
方法创建的。
中间操作
中间操作将一个Stream流转换为另一个Stream流。中间操作总是懒惰的。执行中间操作(如filter()
)实际上并不执行任何过滤,而是创建一个新流。遍历时的新流包含与给定谓词匹配的初始流元素。在这里,您使用filter(Predicate)
按类别筛选书籍。map(Function)
将图书Stream流转换为作者Stream流。
终端操作
终端操作产生结果或副作用。执行终端操作后,流管道被视为已消耗,不能再使用。这里,collect
是一个终端操作。这会将输入元素累积到列表中。
代码示例
让我们看一些流创建示例。您可以在GitHub上(https://github.com/techdozo/articles/tree/master/stream-creation)找到本文的代码。
创建stream流
有很多方法可以在Java中创建stream。让我们看看其中的一些。
来自单个元素的流
Stream of(T… values)
–返回元素为指定值的顺序流。
Stream<String> numbers =
Stream.of("One", "Two", "Three");
所有流操作都可以串行或并行执行。除非明确请求并行性,否则JDK中的流实现将创建串行流。例如,Collection有Collection.stream()
和Collection.parallelStream()
方法,它们分别生成顺序流和并行流;其他流承载方法,如IntStream.range(int,int)
会生成顺序流,但可以通过调用它们的BaseStream.parallel()
方法有效地并行化这些流。
ofNullable(T t)
–返回包含单个元素的顺序流,如果非空,则返回空流。
Stream<String> numbers =
Stream.ofNullable("One");
Stream<String> empty =
Stream.empty();
方法Stream.ofNullable
提供了一个简化的空检查。例如,如果需要将Collection<T>
转换为Stream<T>
,可以执行以下操作:
static Stream<Integer> toStream(Collection<Integer> numbers) {
return Stream.ofNullable(numbers)
.flatMap(Collection::stream);
}
Java9中添加了nullable(T)
方法。
另一种方法是使用可选方法,如:
return Optional
.ofNullable(numbers)
.stream()
.flatMap(Collection::stream);
来自数组的流
您可以使用指定的数组作为源创建顺序流,如下所示:
String[] names =
new String[] {"Jack","Jill"};
Stream<String> stream1 =
Stream.of(names);
Stream<String> stream2 =
Arrays.stream(names);
来自集合的流
可以通过调用集合的默认流方法来创建顺序流。
List<String> namesList =
List.of("Jack", "Jill");
Stream<String> stream =
namesList.stream();
来自Builder的流
使用生成器,您可以通过调用add
和build
方法来构建有序流。
Stream<String> stream =
Stream.<String>builder()
.add("Jack")
.add("Jill")
.build();
流使用Generate
您可以使用generate
方法创建一个无限顺序无序流,其中每个元素都由提供的Supplier
生成。这适用于创建恒定流、随机元素流等。
Stream<Integer> randomNumbers =
Stream
.generate(new Random()::nextInt)
.limit(10);
使用迭代的流
您可以通过调用静态工厂方法iterate(streamiterate(final T seed,final UnaryOperator f))
来创建无限顺序有序流。流的第一个元素由seed
确定,对于n>0
,位置n处的元素将是将函数f应用于n-1
的结果。
Stream<Integer> even =
Stream
.iterate(0, n -> n + 2)
.limit(10);
原始流
专门的原语接口IntStream
、DoubleStream
和LongStream
作为Java8的一部分添加,以高效地处理原语操作。
让我们通过一个例子来理解原语专门化的必要性。考虑一本书课
@Setter
@Getter
public class Book {
private String name;
private Category category;
private double price;
private String author;
private String publisher;
}
要查找所有书籍的累计价格,您可以执行以下操作:
static double priceOfAllBooks(List<Book> books) {
Double allBooksPrice =
books.stream()
.map(Book::getPrice)
.reduce(0d, Double::sum);
return allBooksPrice;
}
在上面的示例中,首先通过调用map(book::getPrice)
将book的流转换为Double的流,然后通过调用terminal
操作reduce(0d,Double::sum)
计算总和。
通过将Stream<Book>
转换为DoubleStream
,可以简化此示例:
static double priceOfAllBooks(List<Book> books) {
double allBooksPrice =
books.stream()
.mapToDouble(Book::getPrice)
.sum();
return allBooksPrice;
}
在上面的示例中,mapToDouble
是一个中间操作,它返回一个DoubleStream
。DoubleStream
是一个double
流,它定义了方便的方法,如sum
、min
、max
等。
Java有三个基本流–IntStream
、DoubleStream
、LongStream
来执行流操作int
、double
和long
基本类型。
创建原始流
您可以使用stream类中的方法从常规流创建基本流。例如,mapToDouble
方法将流转换为DoubleStream
。类似地,Stream类也有mapToInt
和mapToLong
方法。
让我们看看创建基本流的示例。
Of
您可以使用以下静态工厂方法创建顺序有序流:
IntStream intOne =
IntStream.of(1);
IntStream intOneTwo =
IntStream.of(1,2);
DoubleStream doubleOne =
DoubleStream.of(1);
LongStream longOneTwo =
LongStream.of(1,2);
范围
您可以使用原语专门化的range
方法来创建顺序流。
IntStream oneToNine =
IntStream.range(1, 10);
// Range inclusive
IntStream oneToTen =
IntStream.rangeClosed(1, 10);
Generate
您可以使用primitive specialization stream
的generate
方法创建无限顺序无序的原语流。这适用于生成常量流、随机数流等。
IntStream tenOnes =
IntStream.generate(() -> 1).limit(10);
DoubleStream tenRandomDouble =
DoubleStream
.generate(() -> new Random()
.nextDouble()).limit(10);
斐波那契数的生成
您可以对一些有趣的用例使用generate
方法。例如,斐波那契数可以生成为:
public class Fibonacci {
private int prev = 0;
private int curr = 1;
private int next() {
int temp = prev + curr;
prev = curr;
curr = temp;
return curr;
}
public IntStream stream() {
return IntStream
.generate(this::next);
}
}
//Caller
Fibonacci fibonacci =
new Fibonacci();
IntStream fibStream =
fibonacci.stream().limit(10);
迭代
您可以使用迭代方法创建无限顺序流。在IntStream
类中,iterate
方法被定义为IntStream iterate(final int seed,final IntUnaryOperator f)
。
迭代方法通过将函数f应用于初始种子来生成无限流。这将生成一个由seed
、f(seed)
、f(f(seed))
等组成的流。
IntStream evenNumbers =
IntStream
.iterate(0, n -> n + 2)
.limit(10);
创建流的其他方法
创建流还有许多其他方法。例如,您可以组合两个流来创建一个新流。让我们看几个例子。
连接两个字符串
您可以使用流的concat
操作来创建新流。新流的元素是第一个流的所有元素,然后是第二个流的所有元素。如果两个输入流都已排序,则新流已排序;如果其中一个输入流是并行的,则新流已并行。如果结果流已关闭,则两个输入流也将关闭。
Stream<Integer> prime =
Stream
.concat(Stream.of(2, 3),
Stream.of(5, 7, 11));
我们需要对嵌套连接、Stream.concat(Stream.of(..)
、Stream(Stream.of(..)
、Stream.of(..)
、Stream.of(..)
进行谨慎处理,因为它可能导致深层调用链或更糟糕的堆栈溢出错误。
String
Java8添加了实用程序方法来从String对象创建流。
Lines
您可以创建由行终止符分隔的行流,如下所示:
static Stream<String> lines() {
return "Line separated by newline.\nAnother line."
.lines();
}
上面的示例创建了一个由两个元素组成的流:
1. 由换行符分隔的行
2. 另一行
Chars
您可以使用String类的chars
方法来创建字符的IntStream
,如下所示:
static IntStream chars() {
return "XYZ".chars();
}
Random
Random
类有两个有用的方法来生成原始专门化类型的随机数流。
// Infinite Stream
IntStream randomInts =
new Random().ints();
// Fixed Size Stream
IntStream tenRandomInts =
new Random().ints(10);
// Infinite Stream
DoubleStream randomDoubles =
new Random().doubles();
// Fixed Size Stream
DoubleStream tenRandomDoubles =
new Random().doubles(10);
// Infinite Stream
LongStream randomLongs =
new Random().longs();
// Fixed Size Stream
LongStream tenRandomLongs =
new Random().longs(10);
总结
Streams允许我们以声明的方式对数据集合进行计算。您可以创建流管道来执行计算。流管道包括:
- 消息来源
- 零个或多个中间操作
- 终端操作
创建流的方法有很多种。例如,您可以从数组、集合或单个元素创建steam。您甚至可以使用stream类的generate
、iterate
或builder
方法创建流。
Java8还添加了专门的原语流。例如,IntStream
、DoubleStream
和LongStream
都是原始流。
可以从单个元素创建基元流,并使用基元流类的range方法。类似地,可以使用生成器、迭代或生成方法创建基本流。
原文地址:https://techdozo.dev/java-stream-creation/