作者:mobiledu2502899727 | 来源:互联网 | 2023-10-11 15:04
Java8Stream介绍1简介Java8新增了java.util.stream.Stream类,它与java.io包里的InputStream和OutputStream中的流是完
=Java8 Stream介绍=
1 简介
Java8新增了java.util.stream.Stream类,它与java.io包里的InputStream和OutputStream中的流是完全不同的概念。Stream是对集合(Collection)对象功能的增强,它专注于对集合对象进行聚合操作或者大批量数据操作。Stream API借助于同时出现的Lambda表达式,极大地提高编程效率和程序可读性。同时它提供串行和并行两种模式进行聚合操作,并行模式能够充分利用多核处理器的优势,使用Java7开始提供的fork/join并行执行框架来拆分和加速处理过程。java.util.stream是一个函数式语言+多核时代综合影响的产物。
Java的集合API中,仅有极少量的辅助型聚合操作方法,更多的时候需要遍历集合来完成相关的聚合逻辑,这种外部迭代远不够高效。这也是Stream产生的原因之一。
2 流的构成
当我们使用一个流的时候,通常包括三个基本步骤:创建流->中间操作*(Intermediate)->终止操作(Terminal)。
示例:
List list = new ArrayList<>();
long count = list.stream().filter(w -> w.length() > 1).count();
创建流://list.stream()//
中间操作://.filter(w -> w.length() > 1)//
终止操作://.count()//
(NOTE)在使用Stream时,经常会遇到一些带@FunctionalInterface注解的接口,这些称为函数式接口。函数式接口的特点是:里面只能有一个抽象方法。它的用途主要在Lambda表达式和方法引用上。关于FunctionalInterface可参考[[https://docs.oracle.com/javase/8/docs/api/java/lang/FunctionalInterface.html | Java doc]]和[[https://www.cnblogs.com/runningTurtle/p/7092632.html | 这篇博客]]还有[[https://blog.csdn.net/qq_38983577/article/details/81806012 | 这篇]]。
=2.1 创建流的方式=
以下列举一些常见的创建流的方式:
(1) 通过集合创建
任何实现Collection接口的类都可以通过这两个方法来创建流:
default Stream stream(); // 串行流
default Stream parallelStream(); // 并行流
(2) 通过Stream.of()
static Stream of(T… values); // 返回其元素是指定值的顺序排序流
static Stream of(T t); // 返回包含单个元素的顺序Stream
(NOTE)&#8221;…&#8221;表示可变长参数,就是这个位置可以传入任意个该类型的参数或一个该类型数组
例:
Stream stream = Stream.of(“a”, “b”, “c”);
Stream stream2 = Stream.of(new String[]{“a”, “b”, “c”});
(3) 通过Arrsys.stream()
static Stream stream(T[] array);
Arrays.stream()有很多重载方法,可以按需使用。
(4) 通过Stream.Builder创建
相关方法:
static Stream.Builder builder(); // 返回一个Stream的构造器
default Stream.Builder add(T t); // 向要构建的流添加元素
Stream build(); // 构建流,将构建器转换为内置状态
例:
Stream stream = Stream.builder()
.add(“a”)
.add(“b”)
.add(“c”)
.build();
(5) 通过generate()和iterate()创建无限流
无限流通常要跟limit()一起使用,限制流中元素的个数。
// 生成
static Stream generate(Supplier s); // 返回无限顺序无序流,其中每个元素由Supplier.get()提供
//迭代
static Stream iterate(T seed, UnaryOperator f); // 返回有序无限连续Stream,其中每个元素由f迭代产生
Supplier和UnaryOperator都是函数式接口。
例①:
Stream stream = Stream.generate(new Supplier() {
long a = 0, b = 1;
@Override
public Long get() {
long temp = a + b;
a = b;
b = temp;
return a;
}
});
stream.limit(10).forEach(System.out::println);
例②:
Stream stream = Stream.iterate(1, n -> n * 2);
stream.limit(11).forEach(System.out::println);
(6) 从文件创建
Files类中提供了一些用于创建Stream的静态方法:
static Stream lines(Path path); // 从文件中读取所有行作为Stream
static Stream walk(Path start, int maxDepth, FileVisitOption… options); // 返回一个Stream,它由给定的起始根文件遍历文件树的路径构成
// 其它的请查看jdk1.8api文档
例:
try (Stream stream = Files.lines(Paths.get(FILE_PATH), StanderdCharsets.UTF_8)) {
stream.forEach(System.out::println);
} catch (IOException e) {}
(NOTE)&#8221;::&#8221;,双冒号运算符,表示方法引用,格式是&#8221;类名::方法名&#8221;,把方法当作参数传到Stream内部,使得Stream里的每个元素都传入到该方法内部执行一下。方法引用是用来直接访问类或者实例的已经存在的方法或者构造方法。方法引用提供了一种引用而不执行方法的方式,它需要由兼容的函数式接口构成的目标类型上下文。计算时,方法引用会创建函数式接口的一个实例。这个知识点参考[[ https://www.cnblogs.com/xiaoxi/p/7099667.html | Java8之方法引用]]。
(NOTE)Java7中引进了很多新的nio类来取代原来的基于java.io.File的文件IO操作方式,比如这里的Files和Path。Files是一个强大的工具类,提供了一些操作文件的工具方法。Path接口性能更高,在很多场景下可以替换File类。
2.2 中间操作
筛选
(1) filter
Stream filter(Predicate super T> predicate);
predicate是一个函数式接口,表示一个带参数的谓词(给定一个参数,返回boolean类型结果);filter返回由与此给定谓词匹配的此流的元素组成的流,即筛选出符合条件的元素组成新的流。
例:
Stream stream = Stream.of(1, 2, 3, 4).filter(i -> i <= 3); // 1, 2, 3
(2) limit
Stream limit(long maxSize);
返回由此流的元素组成的流,截断长度不能超过maxSize。
例:
Stream stream = Stream.of(1, 2, 3, 4).limit(2L); // 1, 2
(3) skip
Stream skip(long n);
返回一个抛弃了前n个元素的流,若流中元素不满n个,则返回一个空流。
例:
Stream stream = Stream.of(1, 2, 3, 4).skip(2L); // 3, 4
(4) distinct
Stream distinct();
去重,通过流中元素的hashCode()和equals()去除重复元素。自定义的实体使用distinct去重时要重写这两个方法。
例:
Stream stream = Stream.of(1, 1, 2, 3).distinct(); // 1, 2, 3
映射
(5) map
Stream map(Function super T, ? extends R> mapper);
返回由给定函数应用于此流的元素的结果组成的流。
这个方法有三个对于原始类型的变种方法,分别是://mapToInt//,//mapToLong//和//mapToDouble//。这三个方法也比较好理解,比如mapToInt就是把原始Stream转换成一个新的Stream,这个新生成的Stream中的元素都是int类型。之所以会有这样三个变种方法,可以免除自动装箱/拆箱的额外消耗。
例:
Stream stream = Stream.of(1, 2, 3, 4).map(i -> i * 2); // 2, 4, 6, 8
(6) flatMap
Stream flatMap(Function super T, ? extends Stream extends R>> mapper);
返回一个通过将提供的映射函数应用于每个元素而产生的映射流的内容来替换该流的每个元素的结果的流。
由方法签名也可以看出,flatMap接受的映射函数返回值为Stream类型,即每个元素转换得到的是Stream对象,再把子Stream中的元素压缩到父Stream中,是一种扁平化(flat)处理,通常用来处理二维集合。
map和flatMap的区别:map是一维中的映射;flatMap则是将二维的集合映射成一个一维集合(降维),比map的维度深了一层。
同样地,flatMap方法也有//flatMapToInt//,//flatMapToLong//和//flatMapToDouble//三个重载方法。
例:
Stream stream = Stream.of(new Integer[]{1, 2}, new Integer[]{3, 4}).flatMap(Arrays::stream); // 1, 2, 3, 4
Stream stream2 = Stream.of(new Integer[]{1, 2}, new Integer[]{3, 4}).map(Arrays::stream); // 使用map,流中元素仍为流
(NOTE)Tips: ①Function接口原型:Interface Function, T &#8211; 函数的输入类型, R &#8211; 函数的结果类型。②? super: 下界通配符, ? extends: 上界通配符。
排序
(7) sorted
Stream sroted(); // 根据自然顺序排序
Stream sorted(Comparator super T> comparator); // 根据提供的Comparator进行排序
例:
Stream stream = Stream.of(1, 2, 4, 3).sorted(); // 1, 2, 3, 4
Stream stream2 = Stream.of(1, 2, 4, 3).sorted((x, y) -> (x > y) ? -1 : ((x.equals(y)) ? 0 : 1)); // 4, 3, 2, 1
2.3 终止操作
(1) 匹配
boolean allMatch(Predicate super T> predicate); // 返回此流的所有元素是否与提供的谓词匹配
boolean anyMatch(Predicate super T> predicate); // 返回此流的任何元素是否与提供的谓词匹配
boolean noneMatch(Predicate super T> predicate); // 返回此流的元素是否与提供的谓词匹配,即!allMatch
(2) 查找
Optional findFirst(); // 返回流中第一个元素
Optional findAny(); // 返回流中任意一个元素,对串行流来说findAny()和findFirst()返回的结果是一样的;对并行流来说不一样,findAny()性能更好。
查找方法没有参数,可以在查找前用filter加条件。
(3) 计数
long count(); // 返回流中元素总数
(4) 极值
Optional max(Comparator super T> comparator); // 返回流中最大值
Optional min(Comparator super T> comparator); // 返回流中最小值
max和min是要带参数的,例如stream.max(Integer::compareTo)。
(5) 遍历
void forEach(Consumer super T> action); // 遍历流中元素,内部迭代
(6) 规约
Optional reduce(BinaryOperator accumulator); // 将流中元素迭代地累加起来,返回Optional
T reduce(T identity, BinaryOperator accumulator); // 提供一个初值,将流中元素迭代地累加起来,返回T类型
U reduce(U identity, BiFunction accumulator, BinaryOperator combiner); // 提供一个初值,累加方法(二元函数)和结合方法(二元操作符),返回执行结果。
(7) 收集
R collect(Collector super T, A, R> collector);
R collect(Supplier supplier, BiConsumer accumulator, BiConsumer combiner);
将流转换为其它形式,例如收集到List、Set、Map中。