Java Stream使用

这段时间在学数据库和Java,发现Java的Stream实际上和数据库的查询操作非常类似。这里简单介绍Stream的用法,并和Sql Server中的操作联系起来。

此文为初学Stream所写,以后对Stream有更深的理解后会重写

当我们使用一个流的时候,通常包括三个基本步骤:

获取一个数据源(source)

数据转换

执行操作获取想要的结果

每次转换原有 Stream 对象不改变,返回一个新的 Stream 对象(可以有多次转换),这就允许对其操作可以像链条一样排列,变成一个管道,如下图所示。

一、创建 stream

有多种方式生成 Stream Source:

从 Collection 和数组

Collection.stream()

Collection.parallelStream()

Arrays.stream(T array) or Stream.of()

从 BufferedReader

java.io.BufferedReader.lines()

静态工厂

java.util.stream.IntStream.range()

java.nio.file.Files.walk()

自己构建

java.util.Spliterator

其它

Random.ints()

BitSet.stream()

Pattern.splitAsStream(java.lang.CharSequence)

JarFile.stream()

创建Stream示例

// 1. Individual values

Stream stream = Stream.of("a", "b", "c");

// 2. Arrays

String [] strArray = new String[] {"a", "b", "c"};

stream = Stream.of(strArray);

stream = Arrays.stream(strArray);

// 3. Collections

List list = Arrays.asList(strArray);

stream = list.stream();

二、stream 操作

stream的操作分为两大类,一类为中间操作,一类为终端操作。

中间操作:返回值仍然为一个流,不会消耗流

终端操作:返回最终结果;终端操作会消耗掉流,使之不再可用

1.stream.filter()

stream.filter() 是一个中间操作

stream.filter()用于对stream进行某种筛选,stream.filter() 相当于Sql server 中,from ... where ...

在filter()中应当给出筛选条件,准确的说,应该实现Predicate接口,这个接口将被应用于stream中的每一个元素,判断其是否应该被包含在结果stream中。

这个接口只有一个抽象方法待用户实现

抽象方法应该返回一个布尔值,当布尔值为真时,stream.filter()将这个元素包含在结果stream中

stream.filter()使用示例:创建Integer流,然后筛选出偶数

ArrayList arrlist = new ArrayList();

Stream st = arrlist.stream();

Stream st2 = st.filter(new Predicate() {

@Override

public boolean test(Integer arg0) {

return arg0 % 2 == 0;

}

});

还可以用lambda表达式来实现

ArrayList arrlist = new ArrayList();

Stream st = arrlist.stream();

Stream st2 = st.filter((o1)->(o1 % 2 == 0));

关于Predicate接口,它还有.and(),.or(),.negate(),.isEqual()四个默认方法,这里不多介绍。但这些方法也十分常用,对于稍微复杂一点的逻辑就需要使用。

使用Precicate接口需要导入

import java.util.function.Predicate;

2.stream.map()

stream.map()是一个中间操作

stream.map()用于对stream进行某种映射,stream.map() 相当于Sql server 中,select

虽然这么说不太恰当,因为Sql sever 的select实际上时 SQL语言中 \(\sigma , \prod\)的加和,而stream.map() 应该是\(\prod\).

在stream.map()中应该指定转换条件,准确的说,应该实现一个Function()接口,这个接口将被用于stream的每一个元素,将元素按照一定的映射关系映射成新的元素。

Function接口的参数意义

使用Function接口需要导入

import java.util.function.Function;

这个接口只有一个抽象方法待用户实现

抽象方法apply() 接受一个T类型的参数,返回一个R类型的结果

stream.map()使用示例:创建Integer流,然后映射到其原值的两倍

ArrayList arrlist = new ArrayList();

for(int i = 1; i <= 5; i++) {

arrlist.add(i);

}

Stream st = arrlist.stream();

Stream st2 = st.map(new Function() {

@Override

public Integer apply(Integer arg0) {

return arg0 * 2;

}

});

ArrayList ans = new ArrayList();

ans = (ArrayList) st2.collect(Collectors.toList());

for(Integer i : ans) {

System.out.print(i + " ");

}

还可以用lambda表达式来实现

// 初学可以先不这么写

ArrayList ans = (ArrayList)arrlist.stream()

.map((o1)->(2*o1))

.collect(Collectors.toList());

stream.map() 的几种其他形式

IntStream mapToInt(ToIntFunction super T> mapper);

LongStream mapToLong(ToLongFunction super T> mapper);

DoubleStream mapToDouble(ToDoubleFunction super T> mapper);

这三者实际上是对\(Function\) 中 R的固定封装

3.stream.flatMap()

stream.flatMap()是一个中间操作

stream.flatMap()和stream.map()都是进行映射的方法,区别在于,flatMap()处理的元素类型仍是流,flagMap用于将若干个流先拆分成若干个单个元素,再整合成一个流,即流的合并。

简单来说,flatMap()将集合的集合降维成单个元素的集合

实例: 将数组\([[1,2,3],[4,5,6],[7,8],[9]]\)转化为[1,2,3,4,5,6,7,8,9]

ArrayList> list_2 = new ArrayList<>();

list_2.add(new ArrayList<>(Arrays.asList(1,2,3)));

list_2.add(new ArrayList<>(Arrays.asList(4,5,6)));

list_2.add(new ArrayList<>(Arrays.asList(7,8)));

list_2.add(new ArrayList<>(Arrays.asList(9)));

ArrayList list_1 = (ArrayList) list_2.stream()

// list_2.stream() 为 "[1,2,3]" "[4,5,6]" "[7,8]" "[9]" 每个""表示流的不同元素

.flatMap((o1)->(o1).stream())

// 以 o1 = "[1,2,3]"为例,(o1)->(o1).stream() 转化为"1","2","3"

.collect(Collectors.toList());

for(Integer i : list_1) {

System.out.print(i+" ");

}

stream.flagMap()的几种其他形式

IntStream flatMapToInt(Function super T, ? extends IntStream> mapper);

LongStream flatMapToLong(Function super T, ? extends LongStream> mapper);

DoubleStream flatMapToDouble(Function super T, ? extends DoubleStream> mapper);

这三者实际上是对\(Function\) 中 R的固定封装

4.stream.allMatch() ,stream.anyMatch() 和 stream.noneMatch()

stream.allMatch() 和 stream.anyMatch()均为终端操作

传入一个Predicate函数式接口,用于指定条件

5.stream.collect()

stream.collect()为终端操作

Stream的核心在于collect,即对数据的收集。

用法一:将流转化为Collection或Map

Collectors.toCollection() 将数据转换成Collection,只要是Collection的实现都可以,例如ArrayList,HashSet,该方法能够接受一个Collection对象

示例:

//List

Stream.of(1,2,3,4,5,6,7,8,9).collect(Collectors.toCollection(ArrayList::new));

//Set

Stream.of(1,2,3,4,5,6,7,8,9).collect(Collectors.toCollection(HashSet::new));

// Stream.of(1,2,3,4,5,6,7,8,9).collect(Collectors.toList());

// Stream.of(1,2,3,4,5,6,7,8,9).collect(Collectors.toSet());

// Stream.of(1,2,3,4,5,6,7,8,9).collect(Collectors.toMap(key,value));

用法二:字符串聚合规约

Collectors.joining(),拼接,有三个重载方法,底层实现是StringBuilder,通过append方法拼接到一起,并且可以自定义分隔符(这个感觉还是很有用的,很多时候需要把一个list转成一个String,指定分隔符就可以实现了,非常方便)、前缀、后缀。

Student studentA = new Student("20190001", "小明");

Student studentB = new Student("20190002", "小红");

Student studentC = new Student("20190003", "小丁");

//使用分隔符:201900012019000220190003

Stream.of(studentA, studentB, studentC)

.map(Student::getId)

.collect(Collectors.joining());

//使用^_^ 作为分隔符

//20190001^_^20190002^_^20190003

Stream.of(studentA, studentB, studentC)

.map(Student::getId)

.collect(Collectors.joining("^_^"));

//使用^_^ 作为分隔符

//[]作为前后缀

//[20190001^_^20190002^_^20190003]

Stream.of(studentA, studentB, studentC)

.map(Student::getId)

.collect(Collectors.joining("^_^", "[", "]"));

用法三:统计个数

Collectors.counting() 统计元素个数,这个和Stream.count() 作用都是一样的,返回的类型一个是包装Long,另一个是基本long,但是他们的使用场景还是有区别的,这个后面再提。

// Long 8

Stream.of(1,0,-10,9,8,100,200,-80)

.collect(Collectors.counting());

//如果仅仅只是为了统计,那就没必要使用Collectors了,那样更消耗资源

// long 8

Stream.of(1,0,-10,9,8,100,200,-80)

.count();

用法四:集合分组

Collectos.groupingBy()实现集合分组,返回值为一个Map

假如现在有一个实体Student

public class Student {

private String name;

private int score;

private int age;

public Student(String name,int score,int age){

this.name = name;

this.score = score;

this.age = age;

}

public String getName() {

return name;

}

public void setName(String name) {

this.name = name;

}

public int getScore() {

return score;

}

public void setScore(int score) {

this.score = score;

}

public int getAge() {

return age;

}

public void setAge(int age) {

this.age = age;

}

}

现在对其按照Name分组

Map> StrListStrMap = students.stream()

.collect(Collectors.groupingBy(Student::getName));

6.stream.forEach() 和

stream.forEach() 终端操作

stream.forEach()遍历流中的每一个元素,不一定依靠流的顺序,而stream.forEachOrdered()按照流的顺序遍历。

Stream.of(1,2,3,4,5,6).forEach(System.out::println);

7.stream.max() , stream.min() , stream.count()

三个终端操作

stream.max()返回流中的最大值

stream.min()返回流中的最小值

未传入Comparator则填null,默认用Comparable的compareTo函数比较。

stream.count()返回流中元素个数

8.stream.findAny()

返回流中任意一个元素,如果流为空,返回一个空的Optional.

List list = Arrays.asList(1, 2, 3, 4, 5, 6);

Optional any = list.stream().findAny();

Java0steam_Java学习 - Stream 使用相关推荐

  1. Java函数式编程学习——Stream流

    目录 概述 案例数据准备 1. 常用操作 2. 中间操作 filter map distinct sorted limit skip flatMap 3. 终结操作 foreach count max ...

  2. JAVA学习-Stream流的生成、中间操作、终结、收集操作

    Stream流的使用-用于数据集的过滤.映射 1.生成流的操作:通过数据源.集合生成流list.stream() 2.中间操作:一个流可以跟随零个或多个中间操作,其目的主要是打开流,做出某种程度的数据 ...

  3. Java8 Stream API学习

    你可能没意识到Java对函数式编程的重视程度,看看Java 8加入函数式编程扩充多少功能就清楚了.Java 8之所以费这么大功夫引入函数式编程,原因有二: 代码简洁,函数式编程写出的代码简洁且意图明确 ...

  4. 深入理解Java Stream流水线,学到了!

    点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试文章 作者:CarpenterLee cnblogs.com/Carpe ...

  5. 天天在用Stream,那你知道如此强大的Stream的实现原理吗?

    作者:CarpenterLee github.com/CarpenterLee/JavaLambdaInternals 我们已经学会如何使用Stream API,用起来真的很爽,但简洁的方法下面似乎隐 ...

  6. java8 stream案例分析

    java8 stream Stream是java8 推出的一个处理集合类的一个全新的接口,在工作当中经常用到,并且他的预发规则与我们平台的有一点不一样,是流式的处理,非常类似RXJava的一个风格. ...

  7. 简洁又快速地处理集合——Java8 Stream(下)

    上一篇文章我讲解 Stream 流的基本原理,以及它与集合的区别关系,讲了那么多抽象的,本篇文章我们开始实战,讲解流的各个方法以及各种操作 没有看过上篇文章的可以先点击进去学习一下 简洁又快速地处理集 ...

  8. Java中快速处理集合_简洁又快速地处理集合——Java8 Stream(上)

    作者:Howie_Y,系原创投稿 主页:www.jianshu.com/u/79638e5f0743 Java 8 发布至今也已经好几年过去,如今 Java 也已经向 11 迈去,但是 Java 8 ...

  9. Lambda 表达式详解~Stream Pipelines

    前面我们已经学会如何使用Stream API,用起来真的很爽,但简洁的方法下面似乎隐藏着无尽的秘密,如此强大的API是如何实现的呢?比如Pipeline是怎么执行的,每次方法调用都会导致一次迭代吗?自 ...

最新文章

  1. php禁止代理ip访问_php禁止某ip或ip地址段访问的方法
  2. iframe ajax上传,ajax--iframe模拟ajax文件上传效果
  3. 耳机是怎么传输声音的_win7电脑耳机有声音外放没声音怎么办
  4. powerdesigner mysql 反引号_PowerDesigner实用技巧小结 及 导出word,想字段顺序跟模型中一致,如何设置...
  5. 回调 that.setdata 数据不更新_重大利空落地,或损上亿利润,乐普医疗回调近四成...
  6. 与众不同 制作会唱歌的WinRAR - imsoft.cnblogs
  7. view 背景透明
  8. 机器学习如何计算特征的重要性_机器学习之特征工程
  9. deap软件结果分析_绿港科技之窗:基于InDel标记的黄瓜种质资源遗传多样性分析...
  10. 如何避免_小红书如何避免降权!
  11. 主板插槽接口相关释义
  12. Atitit Java制作VCARD vcf 以上就是关于vCard的基本介绍,维基百科(英文)https://en.wikipedia.org/wiki/VCard写的比较全,可惜我看不懂。
  13. c语言添加vmp保护代码,易语言使用vmp加壳保护程序
  14. ENVI53 辐射校正、大气校正、影像裁剪超详细教程
  15. JS导出Excel文件的方式
  16. 计算机技术与软件专业技术资格哪个好考,计算机技术与软件专业技术资格好考吗?考试时间?...
  17. IOS大牛的技术人生的经验与心得
  18. word修订模式怎么彻底关闭_Word怎么关闭审阅修订功能?Word关闭审阅修订显示功能方法...
  19. MySQL 监控软件lepus天兔
  20. 有关Linux内核版本命名规则

热门文章

  1. linux桌面旋转了180度,[多图]回顾每一款默认Ubuntu壁纸
  2. python爬虫运行不出结果_请问这个为什么就是爬不到,运行之后电脑卡的不行,求大佬指导...
  3. 怎么用计算机打分数,电脑excel中分数怎么打出来(图解excel分数输入法)
  4. javascript好文---深入理解定位父级offsetParent及偏移大小
  5. MTU MSS 详解记录
  6. SRAM和SDRAM的区别
  7. cs架构用什么语言开发_用Rust语言开发微信小程序
  8. ie浏览器网页版进入_IE浏览器打开网页速度很慢的解决办法
  9. 终于找到了:NuGet 修改包路径
  10. 第七节:利用CancellationTokenSource实现任务取消和利用CancellationToken类检测取消异常。