文章目录
前言
流提供了一种让我们可以在比集合更高的概念级别上指定计算的数据视图,他是在JavaSE8中引入的,用来以"做什么而非怎么做"的方式处理集合。
1. 从迭代到流的操作
在处理集合时,一般情况下会遍历它的元素,然后再每个元素上执行某种操作。例如:计算某个文本文件长单词(len > 12)的个数。
首先,读取文本文件,然后根据空格拆分为单词集合,然后计算长单词。
import org.apache.commons.io.FileUtils;
import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.List;
/**
* 长单词个数统计
*/
public class WordsCountSample {
public static void main(String[] args) throws IOException {
// 将文件文件读取为一个List,这里用了commons-io,不必纠结
String filename = "D:\\tools\\maven\\apache-maven-3.6.3\\README.txt";
List<String> list = FileUtils.readLines(new File(filename), StandardCharsets.UTF_8);
// 生命变量,长单词个数
int count = 0;
// 遍历每一行
for (String line : list) {
// 根据【空格】分割字符串,生成数组
String[] words = line.split(" ");
// 遍历切割数据数组
for (String word : words) {
// 长度 > 12,计数 + 1
if (word.length() > 12) {
count++;
}
}
}
System.out.println(count);
long count1 = list.stream()
.flatMap(line -> Arrays.stream(line.split(" ")))
.filter(word -> word.length() > 12)
.count();
System.out.println(count1);
}
}
这段代码用了两种写法去统计长单词的个数,传统的迭代和流。使用传统的迭代的话,代码看起来就比较曹锁,而使用流的话,一行代码就搞定,需要开发人员熟悉流式操作。
流和集合看起来很相似,都可以让我们转换和获取数据,但是,然们之间存在限制差距
- 流并不储存元素。这些元素储存在集成集合中,或者按需生成。
- 流的操作不会修改其数据源,而是创建一个新的流
- 流的操作是惰性的,这表示真正需要计算的时候,操作才会执行。如上面的count()
再看上面的代码,stream生成一个流,flatMap按照空格切割生成新的流,filter过滤长度大于12的单词并生成新的流,count计算结果。
这个工作流是操作流时的典型流程
- 创建一个流
- 执行一系列操作,转换、过滤等
- 计算结果
2. 流的创建
java中,有很多的API都可以创建一个流,下面介绍几种简单的
java.util.stream.Stream
- public static Stream of(T… values)
T可变长度的数组 - public static Stream empty()
生成一个不包含任何元素的流 - public static Stream generate(Supplier s)
生成一个无限流,值是通过返回调用函数s构建的 - public static Stream iterate(final T seed, final UnaryOperator f)
产生一个无限流,它的元素包含种子、在种子上调用f产生的值、在前一个元素上调用f产生的值
java.util.Arrays
- public static Stream stream(T[] array)
由数组创建流 - public static Stream stream(T[] array, int startInclusive, int endExclusive)
由数组创建流并执行范围
java.util.regex.Pattern
- public Stream splitAsStream(final CharSequence input)
java.nio.file.Files
- public static Stream lines(Path path) throws IOException
- public static Stream lines(Path path, Charset cs) throws IOException
执行文件编码
3. filter、map、flatMap方法
这几个在流里面比较常用方法,由lambda表达式简化之后,看起来不是那么明显,这里我直接使用函数说明,熟悉之后可以使用lambda表达式简化。
Stream filter(Predicate<? super T> predicate)
filter参数里面,是个Predicate对象,我们可以直接new 一个这样的对象,用于过滤
这里简单说明一下,Stream转换方法里面的参数是函数,这个函数针对流里面的每个元素进行处理。
- 泛型,流里面每个元素类型为String
- s:
for (String item : list) { }
可以理解为item,流里面的元素 - return:true或者false,true表示过滤,在新生成的流里面不会又这个元素
Stream map(Function<? super T, ? extends R> mapper)
map,对象转换,如果你理解泛型,这里就很容易理解了
- String类型的元素s
- Integer类型元素v,这里可以是字符串长度
Stream flatMap(Function<? super T, ? extends Stream<? extends R>> mapper)
把一个元素拆分为多个
小结
- filter:过滤返回值为false的元素,保留返回值为true的元素
- map:流里面元素对象的转换,一对一
- flatMap:一个元素生成多个元素
4. 抽取子流和连接流
- Stream limit(long maxSize)
产生一个新的流,这个流里面包含了当前流前maxSize个元素 - Stream skip(long n)
产生一个新的流,这个流里面是当前流里面除去前n个元素后的所有元素 - public static Stream concat(Stream<? extends T> a, Stream<? extends T> b)
静态方法,合并两个流,生辰新的流
5. 其他转换流
- Stream distinct()
去重 - Stream sorted()
- Stream sorted(Comparator<? super T> comparator)
排序 - Stream peek(Consumer<? super T> action)
产生一个流,它与当前流中的元素相同,在获取每个元素时,会将其传递给action
6. Optional类型
Option对象是一种包装器对象,要么包装了类型T的对象,要么没有包装任何对象
- public T orElse(T other)
产生这个Optional的值,或者Optional为空时,产生other - public T orElseGet(Supplier<? extends T> other)
产生这个Optional的值,或者Optional为空时,产生调用other的结果 - public T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X
Optional为空时,抛出调用exceptionSupplier的结果 - public void ifPresent(Consumer<? super T> consumer)
Optional不为空,将它的值传递给consumer - public<U> Optional<U> map(Function<? super T, ? extends U> mapper)
7. 结果收集
- Iterator iterator()
生成迭代器 - Object[] toArray()
- <A> A[] toArray(IntFunction<A[]> generator)
生成数组 - <R, A> R collect(Collector<? super T, A, R> collector)
- 生成列表
TreeSet treeSet =stream.collect(Collectors.toCollection(TreeSet::new));
- list
List<String> list1 = stream.collect(Collectors.toList());
- set
Set<String> set = stream.collect(Collectors.toSet());
- 了解字符串
String joining = stream.collect(Collectors.joining(","));
- map
Map<String, String> map = stream.collect(Collectors.toMap(s -> s, s -> s));
8. 群组和分区
Collectors.groupingBy,将流进行分组
9. 简约操作
- Optional reduce(BinaryOperator accumulator)
- <U> U reduce(U identity, BiFunction<U, ? super T, U> accumulator, BinaryOperator<U> combiner)
- T reduce(T identity, BinaryOperator accumulator)
解释起来较为复杂,下面一段代码是计算总字符数
Integer integer = stream.map(String::length).reduce(Integer::sum).orElse(null)