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详解的更多相关文章

  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. 解决python3 ,ModuleNotFoundError: No module named 'pip'问题

    今天想要装一下PyYmal第三方库来写一下Python的desired_caps.yaml文件,候发现cmd窗口下无法执行pip命令, 出现了:ModuleNotFoundError: No modu ...

  2. Windows 必备——cmder 一款比cmd牛逼的Win软件

    Windows 必备--cmder 一款比cmd牛逼的Win软件一款Windows环境下的命令行替换工具:cmder这款工具简洁美观易用,支持大部分的linux命令,支持ssh连接Linux,比起自带 ...

  3. Jmeter +Maven+jenkins+eclipse 接口自动化测试

    背景: 首先用jmeter录制或者书写性能测试的脚本,用maven添加相关依赖,把性能测试的代码提交到github,在jenkins配置git下载性能测试的代码,配置运行脚本和测试报告,配置运行失败自 ...

  4. do while 后面要加分号,你大爷的

    do { //do something } while (0) TSfree(url); 这个TSFree 正好是个宏,然后编译就提示错误: error: expected ';' before '_ ...

  5. 豆瓣读书top250数据爬取与可视化

    爬虫–scrapy 题目:根据豆瓣读书top250,根据出版社对书籍数量分类,绘制饼图 搭建环境 import scrapy import numpy as np import pandas as p ...

  6. Python朗读excel中的英文单词

    安装win32com的时候出现了诸多问题,直接贴代码: 1 ''' 2 #利用python朗读excel里面的单词 3 ''' 4 5 #开始导入所需库 6 import xlrd 7 from bs ...

  7. Linux下通用二进制方式安装MySQL

    1.下载glibc版本的MySQL: https://downloads.mysql.com/archives/community/ 2.查看mysql用户和mysql组是否存在(用户和组的信息存在/ ...

  8. 【C++】C++之类型转换

    作者:李春港 出处:https://www.cnblogs.com/lcgbk/p/14209848.html 目录 一.前言 二.static_cast 2.1 使用场景 2.2 实例 三.dyna ...

  9. mybatis实现MySQL数据库的增删改查之二

    这里直接附上代码: 1 package com.qijian.pojo; 2 3 import org.apache.ibatis.type.Alias; 4 5 6 public class Use ...

  10. 感知机:Perceptron Learning Algorithm

    感知机是支持向量机SVM和神经网络的基础 f = sign(wx+b) 这样看起来好像是LR是差不多的,LR是用的sigmoid函数,PLA是用的sign符号函数,两者都是线性分类器,主要的差别在于策 ...