此流非彼流——Stream详解
Stream是什么?
Java从8开始,不但引入了Lambda表达式,还引入了一个全新的流式API:Stream API。它位于java.util.stream包中。
Stream 使用一种类似用 SQL 语句从数据库查询数据的直观方式来提供一种对 Java 集合运算和表达的高阶抽象。
Stream API可以极大提高Java程序员的生产力,让程序员写出高效率、干净、简洁的代码。这种风格将要处理的元素集合看作一种流, 流在管道中传输, 并且可以在管道的节点上进行处理, 比如筛选, 排序,聚合等。元素流在管道中经过中间操作(intermediate operation)的处理,最后由最终操作(terminal operation)得到前面处理的结果。
Stream和IO包下的InputStream和OutputStream一样吗?
划重点:这个Stream不同于java.io的InputStream和OutputStream,它代表的是任意Java对象的序列。两者对比如下:
| java.io | java.util.stream | |
|---|---|---|
| 存储 | 顺序读写的byte或char |
顺序输出的任意Java对象实例 |
| 用途 | 序列化至文件或网络 | 内存计算/业务逻辑 |
这时候大家可能又有疑问了,那么既然是顺序输出的任意Java对象实例,那么和List集合不就相同了吗?
再次划重点:这个Stream和List也不一样,List存储的每个元素都是已经存储在内存中的某个Java对象,而Stream输出的元素可能并没有预先存储在内存中,而是实时计算出来的。
换句话说,List的用途是操作一组已存在的Java对象,而Stream实现的是惰性计算,两者对比如下:
| java.util.List | java.util.stream | |
|---|---|---|
| 元素 | 已分配并存储在内存 | 可能未分配,实时计算 |
| 用途 | 操作一组已存在的Java对象 | 惰性计算 |
关于惰性计算在下面的章节中可以看到。
Stream特点
Stream接口还包含几个基本类型的子接口如IntStream, LongStream 和 DoubleStream。
特点:
- 不存储数据:流是基于数据源的对象,它本身不存储数据元素,而是通过管道将数据源的元素传递给操作。
- 函数式编程:流的操作不会修改数据源,例如
filter不会将数据源中的数据删除。 - 延迟操作:流的很多操作如filter,map等中间操作是延迟执行的,只有到终点操作才会将操作顺序执行。
- 纯消费:流的元素只能访问一次,类似Iterator,操作没有回头路,如果你想从头重新访问流的元素,对不起,你得重新生成一个新的流。
Stream的创建
Stream的创建有多种方式,下面给大家一一列举出来
1、Stream.of()
这种方式一般不常用的,但是测试的时候比较方便
import java.util.stream.Stream;
public class StreamTest {
public static void main(String[] args) {
Stream<String> stream = Stream.of("1", "2", "3", "4");
//forEach()方法相当于内部循环调用
//参数的写法是Lambda表达式
stream.forEach(s -> System.out.println(s));
}
}
关于Lambda表达式,在我的这篇博客中有详细介绍,感兴趣的朋友可以去看一下
2、基于数组或者Collection
import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;
public class StreamTest {
public static void main(String[] args) {
Stream<String> stream1 = Arrays.stream(new String[] { "1", "2", "3" });
Stream<String> stream2 = List.of("X", "Y", "Z").stream();
stream1.forEach(System.out::println);
stream2.forEach(System.out::println);
}
}
这两种创建Stream的方式是我们工作中经常会用到的方式,借助Stream(转化、聚合等方法)可以帮助我们更方便的去输出我们想要的结果
3、其他方式
使用流的静态方法,比如
Stream.of(Object[]),IntStream.range(int, int)或者Stream.iterate(Object, UnaryOperator),如Stream.iterate(0, n -> n * 2),或者generate(Supplier<T> s)如Stream.generate(Math::random)。BufferedReader.lines()从文件中获得行的流。Files类的操作路径的方法,如list、find、walk等。随机数流
Random.ints()。其它一些类提供了创建流的方法,如
BitSet.stream(),Pattern.splitAsStream(java.lang.CharSequence), 和JarFile.stream()。更底层的使用
StreamSupport,它提供了将Spliterator转换成流的方法。
Stream常用API(中间操作)
还记得我们在前面介绍Stream的时候提到了一个惰性计算。惰性计算的特点是:一个Stream转换为另一个Stream时,实际上只存储了转换规则,并没有任何计算发生。中间操作会返回一个新的流,它不会修改原始的数据源,而且是由在终点操作开始的时候才真正开始执行。
1、distinct
distinct保证输出的流中包含唯一的元素,它是通过Object.equals(Object)来检查是否包含相同的元素。
import java.util.stream.Stream;
public class StreamTest {
public static void main(String[] args) {
Stream<String> stream = Stream.of("a", "b", "c", "b","c","d").distinct();
stream.forEach(System.out::println);
}
}
//输出结果
a
b
c
d
2、filter
从字面看是过滤的意思,过滤掉不满足条件的数据
import java.util.stream.IntStream;
public class StreamTest {
public static void main(String[] args) {
IntStream stream = IntStream.range(1, 10).filter(i -> i % 2 == 0); //filter中的参数是过滤条件
stream.forEach(System.out::println);
}
}
//输出结果
2
4
6
8
3、map
map方法可以将流中的值映射成另外的值,比如将字符串全部转化成小写
import java.util.stream.Stream;
public class StreamTest {
public static void main(String[] args) {
Stream<String> stream = Stream.of("Hello WORLD HELLO Life").map(s -> s.toLowerCase());
stream.forEach(System.out::println);
}
}
//输出结果
hello world hello life
从输出结果我们可以看到,字符串全部转化成小写字符了
4、limit
limit方法指定流的元素数列,类似于Mysql中的limit方法
import java.util.stream.Stream;
public class StreamTest {
public static void main(String[] args) {
Stream<String> stream = Stream.of("1", "2", "3", "4", "5", "6").limit(3); //取三条
stream.forEach(System.out::println);
}
}
// 输出结果
1
2
3
5、peek
import java.util.stream.Stream;
public class StreamTest {
public static void main(String[] args) {
Stream<String> stream = Stream.of("Hello WORLD HELLO Life").peek(s -> {
String peek = s.toLowerCase();
System.out.println(peek);
});
stream.forEach(System.out::println);
}
}
//输出结果
hello world hello life
Hello WORLD HELLO Life
有没有发现出一些东西?
我们将这段代码用上面的map方法实现一下
import java.util.stream.Stream;
public class StreamTest {
public static void main(String[] args) {
Stream<String> stream = Stream.of("Hello WORLD HELLO Life").map(s -> {
String peek = s.toLowerCase();
System.out.println(peek);
return peek;
});
stream.forEach(System.out::println);
}
}
// 输出结果
hello world hello life
hello world hello life
peek方法的定义如下:
Stream<T> peek(Consumer<? super T> action);
peek方法接收一个Consumer的入参。了解λ表达式的应该明白 Consumer的实现类 应该只有一个方法,该方法返回类型为void。
而map方法的入参为 Function。
<R> Stream<R> map(Function<? super T, ? extends R> mapper);
我们发现Function 比 Consumer 多了一个 return。这也就是peek 与 map的区别了。
6、skip
skip返回丢弃了前n个元素的流,如果流中的元素小于或者等于n,则返回空的流。
7、sorted
sorted()将流中的元素按照自然排序方式进行排序
import java.util.stream.Stream;
public class QueryTest {
public static void main(String[] args) {
//自定义排序
customSort();
//自然排序
naturalSort();
}
public static void customSort() {
Stream stream = Stream.of("hello", "I", "love", "you").sorted((str1, str2) -> {
// 自定义排序规则
if (str1 == null) {
return -1;
}
if (str2 == null) {
return 1;
}
return str1.length() - str2.length();
});
System.out.println("-----------自定义排序-----------");
stream.forEach(System.out::println);
}
public static void naturalSort() {
Stream<String> stream = Stream.of("hello", "I", "love", "you").sorted();
System.out.println("-----------自然排序------------");
stream.forEach(System.out::println);
}
}
// 输出结果
-----------自定义排序-----------
I
you
love
hello
-----------自然排序------------
I
hello
love
you
如果我们直接调用sorted()方法,那么将按照自然排序,如果我们希望元素按照我们想要的结果来排序,需要自定义排序方法,sorted(Comparator<? super T> comparator)可以指定排序的方式。如果元素没有实现Comparable,则终点操作执行时会抛出java.lang.ClassCastException异常。
Stream常用API(终点操作)
1、max、min、count
max:获取最大值
min:获取最小值
count:返回流的数量
2、reduce
reduce操作可以实现从一组元素中生成一个值,max()、min()、count()等都是reduce操作,将他们单独设为函数只是因为常用。reduce()的方法定义有三种重写形式:
Optional<T> reduce(BinaryOperator<T> accumulator)
T reduce(T identity, BinaryOperator<T> accumulator)
<U> U reduce(U identity, BiFunction<U,? super T,U> accumulator, BinaryOperator<U> combiner)
3、count
获取Stream数量
package com.mybatisplus;
import java.util.stream.Stream;
public class QueryTest {
public static void main(String[] args) {
long count = Stream.of("a", "b", "A", "a", "c", "a").count();
System.out.println(count);
}
}
//输出结果 6
4、Match
anyMatch表示,判断的条件里,任意一个元素成功,返回true
allMatch表示,判断条件里的元素,所有的都是,返回true
noneMatch跟allMatch相反,判断条件里的元素,所有的都不是,返回true
package com.mybatisplus;
import java.util.stream.Stream;
public class QueryTest {
public static void main(String[] args) {
boolean b1 = Stream.of("a", "b", "A", "a", "c", "a").anyMatch(str -> str.equals("a"));
boolean b2 = Stream.of("a", "b", "A", "a", "c", "a").allMatch(str -> str.equals("a"));
boolean b3 = Stream.of("a", "b", "A", "a", "c", "a").noneMatch(str -> str.equals("a"));
System.out.println("b1 = " + b1);
System.out.println("b2 = " + b2);
System.out.println("b3 = " + b3);
}
}
// 输出结果
b1 = true
b2 = false
b3 = false
此流非彼流——Stream详解的更多相关文章
- 转:WCF传送二进制流数据基本实现步骤详解
来自:http://developer.51cto.com/art/201002/185444.htm WCF传送二进制流数据基本实现步骤详解 2010-02-26 16:10 佚名 CSDN W ...
- 大数据入门第十六天——流式计算之storm详解(二)常用命令与wc实例
一.常用命令 1.提交命令 提交任务命令格式:storm jar [jar路径] [拓扑包名.拓扑类名] [拓扑名称] torm jar examples/storm-starter/storm-st ...
- 大数据入门第十六天——流式计算之storm详解(一)入门与集群安装
一.概述 今天起就正式进入了流式计算.这里先解释一下流式计算的概念 离线计算 离线计算:批量获取数据.批量传输数据.周期性批量计算数据.数据展示 代表技术:Sqoop批量导入数据.HDFS批量存储数据 ...
- JDK1.8中的Stream详解
Stream简介 Stream 作为 Java 8 的一大亮点,它与 java.io 包里的 InputStream 和 OutputStream 是完全不同的概念.它也不同于 StAX 对 XML ...
- JMeter学习-023-JMeter 命令行(非GUI)模式详解(一)-执行、输出结果及日志、简单分布执行脚本
前文 讲述了JMeter分布式运行脚本,以更好的达到预设的性能测试(并发)场景.同时,在前文的第一章节中也提到了 JMeter 命令行(非GUI)模式,那么此文就继续前文,针对 JMeter 的命令行 ...
- 爱你不容易 —— Stream详解
作为前端,我们常常会和 Stream 有着频繁的接触.比如使用 gulp 对项目进行构建的时候,我们会使用 gulp.src 接口将匹配到的文件转为 stream(流)的形式,再通过 .pipe() ...
- gulp源码解析(一)—— Stream详解
作为前端,我们常常会和 Stream 有着频繁的接触.比如使用 gulp 对项目进行构建的时候,我们会使用 gulp.src 接口将匹配到的文件转为 stream(流)的形式,再通过 .pipe() ...
- Java8之Stream详解
Java8中提供了Stream对集合操作作出了极大的简化,学习了Stream之后,我们以后不用使用for循环就能对集合作出很好的操作. 一.流的初始化与转换 Java中的Stream的所有操作 ...
- 大数据入门第十六天——流式计算之storm详解(三)集群相关进阶
一.集群提交任务流程分析 1.集群提交操作 参考:https://www.jianshu.com/p/6783f1ec2da0 2.任务分配与启动流程 参考:https://www.cnblogs.c ...
随机推荐
- mysql主从同步错误
一.主从同步报错 mysql> show slave status\G; *************************** 1. row ************************* ...
- ORACLE 10g、11g批量刷新用户密码
一.ORACLE 10g批量刷新用户密码 select 'alter user ' || username ||' identified by values '''||password ||''';' ...
- 跨站点脚本编制 - SpringBoot配置XSS过滤器(基于mica-xss)
1. 简介 XSS,即跨站脚本编制,英文为Cross Site Scripting.为了和CSS区分,命名为XSS. XSS是最普遍的Web应用安全漏洞.这类漏洞能够使得攻击者嵌入恶意脚本代码 ...
- CSS—— div+css
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title> ...
- CCNP之静态路由实验报告
静态路由实验报告 一.实验要求: 1.内网IP基于172.16.0.0/16自行子网划分 2.除了R2--R4路由器各有两个环回接口 3.R1下的PC自动获取IP地址 4 ...
- SpringBoot全局异常拦截
SpringBoot全局异常捕获 使用到的技能 @RestControllerAdvice或(@ControllerAdvice+@ResponseBody) @ExceptionHandler 代码 ...
- C语言I博客作业1
1 .班级链接: https://edu.cnblogs.com/campus/zswxy/SE2020-3 2 .作业要求链接: https://edu.cnblogs.com/campus/zsw ...
- jstat gcutil
QQA: jstat gcutil 的输出是什么意思 当 Java 程序有性能问题时,尤其是响应时间有突然变化时,最好第一时间查看 GC 的状态.一般用 jstat -gcutil <pid&g ...
- 网易163 docker镜像
$ sudo echo "DOCKER_OPTS=\"--registry-mirror=http://hub-mirror.c.163.com\"" > ...
- Jmeter(三十四) - 从入门到精通进阶篇 - 参数化(详解教程)
1.简介 前边三十多篇文章主要介绍的是Jmeter的一些操作和基础知识,算是一些初级入门的知识点,从这一篇开始我们就来学习Jmeter比较高级的操作和深入的知识点了.今天这一篇主要是讲参数化,其实前边 ...