菜鸟为了巩固所写
目录
一、概述
- 为了支持级联调用,所有流的“中间操作”都会返回一个流对象。
- 但我们总需有一个“初始”的流对象,以便基于它开始构建一个流处理管线。
- 下面介绍如何从原始数据源中创建这个“初始”的流对象。
二、创建Stream
综合示例1
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import java.util.function.IntSupplier;
import java.util.stream.IntStream;
import java.util.stream.Stream;
public class CreateStream {
public static void main(String[] args) {
createForCollection();
createUseStreamOf();
createUseStreamBuilder();
createNumberStream();
createEmptyStream();
}
// 基于集合对象创建流
private static void createForCollection() {
//创建一个集合对象
Set<String> names = new HashSet<>();
names.add("Tom");
names.add("Jacket");
// 基于集合对象创建流
Stream<String> sequentialStream = names.stream();
//基于集合对象创建并行流( parallel stream)
Stream<String> parallelStream = names.parallelStream();
//基于数组创建流
int[] numbers = {2, 3, 5, 7, 11, 13};
var arrayStream = Arrays.stream(numbers);
}
//使用Stream.of方法创建流
private static void createUseStreamOf() {
//使用可变参数创建流
var strStream = Stream.of("Java", "Kotlin", "C#");
//基于数组创建流
String[] names = {"Ken", "Jeff", "Chris", "Ellen"};
Stream<String> nameStream = Stream.of(names);
}
//使用StreamBuilder创建流
private static void createUseStreamBuilder() {
Stream<String> stream = Stream.<String>builder()
.add("Ken")
.add("Jeff")
.add("Chris")
.add("Ellen")
.build();
}
//创建原始数值类型构成的流
private static void createNumberStream() {
//使用可变参数构建数值流
IntStream stream = IntStream.of(1, 1, 2, 3, 5);
//使用int数组构建流
int[] values = {1, 2, 3, 4};
IntStream stream2 = Arrays.stream(values);
//生成的流包容 [0,100) 区间的所有整数
IntStream zeroToNinetyNine = IntStream.range(0, 100);
//生成的流包容 [0,100]区间的所有整数
IntStream zeroToHundred = IntStream.rangeClosed(0, 100);
}
//构建一个不包容任何元素的流
private static void createEmptyStream() {
//构建一个空的字符串流
Stream<String> stream = Stream.empty();
//构建一个空的整数流
IntStream numbers = IntStream.empty();
}
}
1、基于集合创建Stream
所有集合对象都实现的Collection 接口定义了一个 Stream() 或parallelStream 方法,可以通过它来创建流对象:
// 基于集合对象创建流
private static void createForCollection() {
//创建一个集合对象
Set<String> names = new HashSet<>();
names.add("Tom");
names.add("Jacket");
// 基于集合对象创建流
Stream<String> sequentialStream = names.stream();
//基于集合对象创建并行流( parallel stream)
Stream<String> parallelStream = names.parallelStream();
//基于数组创建流
int[] numbers = {2, 3, 5, 7, 11, 13};
var arrayStream = Arrays.stream(numbers);
}
JDK中的 Arrays 类提供了直接基于现有数组创建流的静态方法 stream()stream(),注意一下流的类型比较特殊,是 IntStream 。
2、使用Stream.of创建流
Stream接口定义了一个 of 方法,可以用于创建流
//使用Stream.of方法创建流
private static void createUseStreamOf() {
//使用可变参数创建流
var strStream = Stream.of("Java", "Kotlin", "C#");
//基于数组创建流
String[] names = {"Ken", "Jeff", "Chris", "Ellen"};
Stream<String> nameStream = Stream.of(names);
}
在Java 标准库中, of 方法的实现实际上被委托给java.util.Arrays 类定义的 stream 方法,基于数组创建流。
3、使用Stream.Builder<T>
使用Stream.Builder<T 也 能创建流
//使用StreamBuilder创建流
private static void createUseStreamBuilder() {
Stream<String> stream = Stream.<String>builder()
.add("Ken")
.add("Jeff")
.add("Chris")
.add("Ellen")
.build();
}
4、基于数组构建流
5、构建一个不包容任何元素的流
//构建一个不包容任何元素的流
private static void createEmptyStream() {
//构建一个空的字符串流
Stream<String> stream = Stream.empty();
//构建一个空的整数流
IntStream numbers = IntStream.empty();
}
综合示例2
import java.util.function.IntPredicate;
import java.util.function.IntUnaryOperator;
import java.util.function.Supplier;
import java.util.function.UnaryOperator;
import java.util.stream.IntStream;
import java.util.stream.Stream;
public class StreamElementFactory {
public static void main(String[] args) {
// 示例1:使用 Stream.generate 方法
generateExample();
// 示例2:使用 Stream.iterate 方法
iterateExample();
// 示例3:使用带条件的 Stream.iterate 方法
useIterateWithPredicate();
}
// 示例1:使用 Stream.generate 方法生成流
private static void generateExample() {
// 定义一个随机数生成器,生成 [0, 100) 范围内的随机整数
Supplier<Integer> intFactory = () -> (int) (Math.random() * 100);
// 使用 Stream.generate 创建一个无限流,并限制为前 10 个元素
Stream<Integer> stream = Stream.generate(intFactory).limit(10);
// 打印流中的元素
stream.forEach(System.out::println);
}
// 示例2:使用 Stream.iterate 方法生成流
private static void iterateExample() {
// 初始值为 1
int seed = 1;
// 定义一个生成规则:a[n] = a[n-1] + 2
UnaryOperator<Integer> intFactory = n -> n + 2;
// 使用 Stream.iterate 创建一个无限流,并限制为前 10 个元素
Stream<Integer> stream = Stream.iterate(seed, intFactory).limit(10);
// 打印流中的元素
stream.forEach(System.out::println);
}
// 示例3:使用带条件的 Stream.iterate 方法生成流
// 生成从 0 开始,每次加 5 的数列,直到元素大于 100 为止
private static void useIterateWithPredicate() {
// 定义一个条件:元素必须小于等于 100
IntPredicate predicate = n -> n <= 100;
// 定义生成规则:每次加 5
IntUnaryOperator next = n -> n + 5;
// 使用带条件的 IntStream.iterate 方法生成流
IntStream.iterate(0, predicate, next)
.forEach(System.out::println);
}
}
1、“基于工厂函数”构建无限流
我们可以定义一个函数,这个函数可以帮助我们构建出流的元素,这个函数可以看成是生成流元素的“元素工厂”,然后使用这个工厂生产出来的“元素”构建出流。
// 示例1:使用 Stream.generate 方法生成流
private static void generateExample() {
// 定义一个随机数生成器,生成 [0, 100) 范围内的随机整数
Supplier<Integer> intFactory = () -> (int) (Math.random() * 100);
// 使用 Stream.generate 创建一个无限流,并限制为前 10 个元素
Stream<Integer> stream = Stream.generate(intFactory).limit(10);
// 打印流中的元素
stream.forEach(System.out::println);
}
注意:这种方式可以构建出元素数目无限的“无限流”,因此,通常都会使用 limit() 方法限定要生成 的元素个数。
2、“基于迭代函数”构建无限流
有些流的元素需要依据它的前一个元素才能确定,对于这种情况,可以使用 Stream.iterate() 方法构建流对象:
// 示例2:使用 Stream.iterate 方法生成流
private static void iterateExample() {
// 初始值为 1
int seed = 1;
// 定义一个生成规则:a[n] = a[n-1] + 2
UnaryOperator<Integer> intFactory = n -> n + 2;
// 使用 Stream.iterate 创建一个无限流,并限制为前 10 个元素
Stream<Integer> stream = Stream.iterate(seed, intFactory).limit(10);
// 打印流中的元素
stream.forEach(System.out::println);
}
-
Stream.iterate(seed, UnaryOperator)
:Stream.iterate
是一个静态方法,用于生成一个无限流。seed
:初始值,这里是1。UnaryOperator
:生成规则,这里是 n-> n+2
。
综合示例3(Fibonacci)
import java.util.function.IntSupplier;
import java.util.stream.IntStream;
import java.util.stream.Stream;
public class CreateFibonacci {
public static void main(String[] args) {
getFibonacciUseIterator(10);
getFibonacciUseGenerate(10);
}
private static void getFibonacciUseIterator(int limit) {
//斐波那契元组序列
Stream.iterate(new int[]{0, 1}, t -> new int[]{t[1], t[0] + t[1]})
.limit(limit) //生成20个元素
.map(t -> t[0]) //只提取出数组中的第1个数(它就是要求的斐波那契数)
.forEach(System.out::println);
}
static void getFibonacciUseGenerate(int limit) {
IntSupplier fib = new IntSupplier() {
private int previous = 0;
private int current = 1;
public int getAsInt() {
int oldPrevious = this.previous;
int nextValue = this.previous + this.current;
this.previous = this.current;
this.current = nextValue;
return oldPrevious;
}
};
IntStream.generate(fib)
.limit(limit)
.forEach(System.out::println);
}
}
解释1
在 Stream.iterate
中,第一次迭代直接返回初始值(seed
),并不会调用生成规则(UnaryOperator
)。
-
Stream.iterate(seed, UnaryOperator)
:Stream.iterate
是一个静态方法,用于生成一个无限流。seed
:初始值,这里是new int[]{0, 1}
,表示斐波那契数列的前两个数。UnaryOperator
:生成规则,这里是t -> new int[]{t[1], t[0] + t[1]}
。t[1]
:当前元组的第二个数。t[0] + t[1]
:当前元组中两个数的和。- 每次迭代都会生成新的元组
[t[1], t[0] + t[1]]
,表示下一个斐波那契数对。
综合示例4(生成随机数流)
import java.util.Random;
import java.util.stream.Stream;
public class CreateRandomNumberStream {
public static void main(String[] args) {
useGenerate(1, 100);
useRandomClass();
}
//使用generate()方法生成[from,to]之间的随机整数
private static void useGenerate(int from, int to) {
Stream.generate(Math::random)
.limit(5)
.map(doubleNum -> (int) (doubleNum * (to - from + 1)) + 1)
.forEach(System.out::println);
}
//基于JDK所提供的Random类方法生成随机数流
private static void useRandomClass() {
//生成[0,100)区间中的随机整数
new Random()
.ints(0, 100)
.limit(5)
.forEach(System.out::println);
//生成[100,200)区间内的随机浮点数
new Random()
.doubles(100, 200)
.limit(5)
.forEach(System.out::println);
}
}
解释1
//使用generate()方法生成[from,to]之间的随机整数
private static void useGenerate(int from, int to) {
Stream.generate(Math::random)
.limit(5)
.map(doubleNum -> (int) (doubleNum * (to - from + 1)) + 1)
.forEach(System.out::println);
}
使用Stream.generate 方法,配合 JDK 中所提供的 Math.random方法,可以很方便地生成特定区间内的随机整数。
Stream.generate是一个静态方法,用于创建无限流,Stream.generate可以没有接收形参,只有返回值,也就是说Stream.generate(Math::random)里面的Math::random可以改成()->Math.random()。
解释2
鉴于程序中经常需要生成随机的整数和浮点数,JDK8 中为 Random 类添加了ints()/longs()/doubles() 方法创建相应的数值流。
//基于JDK所提供的Random类方法生成随机数流
private static void useRandomClass() {
//生成[0,100)区间中的随机整数
new Random()
.ints(0, 100)
.limit(5)
.forEach(System.out::println);
//生成[100,200)区间内的随机浮点数
new Random()
.doubles(100, 200)
.limit(5)
.forEach(System.out::println);
}
数值流
- 引入数值流的主要考虑, 是避免装箱损失。
- Java 8引入了三个原始 类型流 接口: IntStream 、DoubleStream 和 LongStream ,其流中的元素为 int 、 long和 double 这样的原始数据类型,在处理数据时,可以避免装箱带来的性能损失。
示例5(使用装箱流)
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
public class UseBoxedStream {
public static void main(String[] args) {
//以下代码无法编译
//IntStream.of(3, 1, 4, 1, 5, 9)
// .collect(Collectors.toList());
//解决之道:将int用boxed方法转换为Integer,就可以编译了
List<Integer> ints = IntStream.of(3, 1, 4, 1, 5, 9)
.boxed()
.collect(Collectors.toList());
//输出:[3, 1, 4, 1, 5, 9]
System.out.println(ints);
//另一种方式:使用mapToObject,将其转换为对象
List<Integer> ints2 = IntStream.of(3, 1, 4, 1, 5, 9)
.mapToObj(Integer::valueOf)
.collect(Collectors.toList());
//输出:[3, 1, 4, 1, 5, 9]
System.out.println(ints2);
}
}
这里首先要知道collect()是Stream API的一个终端操作,用于将产生的流收集到一个地方,这里collect(Collectors.toList())就是将产生的流收集到List类型。
综合示例6(基于文件夹和文件创建流)
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.stream.Stream;
public class GetStreamFromJDK {
public static void main(String[] args) {
//useNIOFileStream();
listFileTree();
}
private static void useNIOFileStream() {
//lines()方法,返回的就是一个字符串流
try (Stream<String> lines = Files.lines(
Paths.get("data.txt"))) {
lines.forEach(System.out::println);
} catch (IOException ex) {
System.out.println(ex.getMessage());
}
}
// 列出当前文件夹下的所有文件
public static void listFileTree() {
Path dir = Paths.get("");
System.out.printf(" %s contains:\n", dir.toAbsolutePath());
//walk方法返回一个Stream
try (Stream<Path> fileTree = Files.walk(dir)) {
fileTree.forEach(System.out::println);
} catch (IOException e) {
e.printStackTrace();
}
}
}