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. ORACLE启用非默认监听端口

    1.修改listener.ora文件 LISTENER_TEST =         (DESCRIPTION =                 (ADDRESS = (PROTOCOL = TCP ...

  2. 记录一次mac访问Windows共享目录失败

    一,起因 起因,有人联系我说他们的mac电脑连接不上Windows的共享目录,Windows的电脑连接正常,没有报错,连接框抖两下就没了 二,排查问题 1,我自己想mstsc登陆服务器看看,结果服务器 ...

  3. 使用VNC远程linux机器

    一,在Windows机器上下载vnc客户端 vnc viewer是一款vnc远程客户端,可以很方便的从Windows机器远程安装了图形界面和vnc服务器的linux机器. 从官网下载https://w ...

  4. IDEA 2020.3 更新了,机器学习都整上了

    Hello,大家好,我是楼下小黑哥~ 上周 Java 开发申请神器 IDEA 2020.3 新版正式发布: 小黑哥第一时间就在开发机上更新了新版本,并且完整体验了两周了. 下面介绍一下这个版本的主要功 ...

  5. Jmeter二次开发——基于Java请求

    简述 这近几年,越来越多非http的协议需要进行性能测试,包括不仅限于各类rpc.mq.缓存等.对于这些协议,市面上可能没有现成的工具可以直接使用,这个时候,我们可以自己动手,通过编写相应的JavaS ...

  6. 多任务-python实现-继承Thread类,单独编写一个类(2.1.2)

    @ 目录 1.thread类 1.thread类 threding代码实现 import threading import time class MyThread(threading.Thread): ...

  7. Docker实例开机启动

    部署项目服务器时,为了应对停电等情况影响正常web项目的访问,会把Docker容器设置为开机自动启动. 在使用docker run启动容器时,使用--restart参数来设置: docker run ...

  8. Python将文件夹下的文件名写入excel方便统计

    如题,贴代码: 1 ''' 2 #python将某文件夹下的文件名存储到excel中 3 ''' 4 5 #导入所需模块 6 import os 7 import xlwt 8 9 #定义要处理的文件 ...

  9. sqlserver varchar和Nvarchar区别

    sql server中的varchar和Nvarchar有什么区别?   答:varchar(n)长度为 n 个字节的可变长度且非 Unicode 的字符数据.n 必须是一个介于 1 和 8,000 ...

  10. EF快速入门--直接修改(简要介绍ObjectContext处理机制)

    原博文 http://www.cnblogs.com/fly_dragon/archive/2011/06/05/2073084.html ObjectContext的处理机制 ObjectConte ...