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.ioInputStreamOutputStream,它代表的是任意Java对象的序列。两者对比如下:

java.io java.util.stream
存储 顺序读写的bytechar 顺序输出的任意Java对象实例
用途 序列化至文件或网络 内存计算/业务逻辑

这时候大家可能又有疑问了,那么既然是顺序输出的任意Java对象实例,那么和List集合不就相同了吗?

再次划重点:这个StreamList也不一样,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类的操作路径的方法,如listfindwalk等。

  • 随机数流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详解的更多相关文章

  1. 转:WCF传送二进制流数据基本实现步骤详解

    来自:http://developer.51cto.com/art/201002/185444.htm WCF传送二进制流数据基本实现步骤详解 2010-02-26 16:10 佚名 CSDN   W ...

  2. 大数据入门第十六天——流式计算之storm详解(二)常用命令与wc实例

    一.常用命令 1.提交命令 提交任务命令格式:storm jar [jar路径] [拓扑包名.拓扑类名] [拓扑名称] torm jar examples/storm-starter/storm-st ...

  3. 大数据入门第十六天——流式计算之storm详解(一)入门与集群安装

    一.概述 今天起就正式进入了流式计算.这里先解释一下流式计算的概念 离线计算 离线计算:批量获取数据.批量传输数据.周期性批量计算数据.数据展示 代表技术:Sqoop批量导入数据.HDFS批量存储数据 ...

  4. JDK1.8中的Stream详解

    Stream简介 Stream 作为 Java 8 的一大亮点,它与 java.io 包里的 InputStream 和 OutputStream 是完全不同的概念.它也不同于 StAX 对 XML ...

  5. JMeter学习-023-JMeter 命令行(非GUI)模式详解(一)-执行、输出结果及日志、简单分布执行脚本

    前文 讲述了JMeter分布式运行脚本,以更好的达到预设的性能测试(并发)场景.同时,在前文的第一章节中也提到了 JMeter 命令行(非GUI)模式,那么此文就继续前文,针对 JMeter 的命令行 ...

  6. 爱你不容易 —— Stream详解

    作为前端,我们常常会和 Stream 有着频繁的接触.比如使用 gulp 对项目进行构建的时候,我们会使用 gulp.src 接口将匹配到的文件转为 stream(流)的形式,再通过 .pipe() ...

  7. gulp源码解析(一)—— Stream详解

    作为前端,我们常常会和 Stream 有着频繁的接触.比如使用 gulp 对项目进行构建的时候,我们会使用 gulp.src 接口将匹配到的文件转为 stream(流)的形式,再通过 .pipe() ...

  8. Java8之Stream详解

    Java8中提供了Stream对集合操作作出了极大的简化,学习了Stream之后,我们以后不用使用for循环就能对集合作出很好的操作.   一.流的初始化与转换   Java中的Stream的所有操作 ...

  9. 大数据入门第十六天——流式计算之storm详解(三)集群相关进阶

    一.集群提交任务流程分析 1.集群提交操作 参考:https://www.jianshu.com/p/6783f1ec2da0 2.任务分配与启动流程 参考:https://www.cnblogs.c ...

随机推荐

  1. JavaSE19-IO特殊流和Properties集合

    1.IO特殊操作流 1.1 标准输入流 System类中有两个静态的成员变量 public static final InputStream in:标准输入流.通常该流对应于键盘输入或由主机环境或用户 ...

  2. 微信小程序自动化,记录趟过的坑!

    项目思想:关键字+数据驱动混合测试 基于Android-微信小程序自动化的关键是:webview的切换 对于微信App来说如何从NATIVE切换到webview的过程 测试版本信息 1.微信版本:7. ...

  3. kali 2020安装docker

    环境准备 kali虚拟机2020.01x64位版本 安装docker官方文档:https://docs.docker.com/engine/installation/linux/docker-ce/d ...

  4. Erlang那些事儿之正儿八经的前言

    说在前面,为啥要码这些,并不是因为喜欢它,恰恰相反,我非常讨厌Erlang(真香警告)这位二郎神(Erlang的谐音),讨厌它的语法,讨厌它不变的变量,讨厌它的一切. 曾经的我,一听到这个语言,我就打 ...

  5. Java篇:Docker的介绍安装 和常用命令

    文章目录 为什么 出现docker Docker的简介 容器(Container) 镜像(Image) 仓库(Repository) Docker的安装 查看容器 删除镜像 删除容器 部署应用 以my ...

  6. UWP ListView添加不同样式

    先看效果: 使用ListView的ItemTemplateSelector <ListView IsItemClickEnabled="True" ItemContainer ...

  7. Core3.0类库项目引用Microsoft.AspNetCore

    前言 参考 https://www.cnblogs.com/puzi0315/p/12190989.html 步骤 修改Project.Sdk 添加OutputType <Project Sdk ...

  8. win7开机登录界面壁纸修改

    1.选择一张自己喜欢的图(一定要是jpg格式,亲测png格式不行),分辨率最好和自己电脑的分辨率差不多. 2.将图片改名为"backgroundDefault.jpg": 3.按下 ...

  9. WPF 中的相关样式

    <Image Name="icon" Width="40" Height="40"  Source="/Resources/ ...

  10. 小马哥讲Spring栈核心编程思想 Spring IoC+Bean+Framework

    小马哥出手的Spring栈核心编程思想课程,可以说是非常专业和权威的Spring课程.课程主要的方向与核心是Spring Framework总览,带领同学们重新认识重新认识IoC,Spring IoC ...