Java流(Stream)

​ Java 8添加了一个新的抽象称为流Stream,可以让我们以一种声明的方式处理数据。当然新特性什么的就不说了,毕竟现在Java 16都出了,这算老特性了。只是平时工作中还是经常能用到,确实比较方便,所以今天写个总结。

​ 为了方便理解,就少来点理论,直接以实战代码为主进行分析。想要了解实现原理,建议看看源码,这里只讲使用方法,不扯远了,还是先来看看它的定义:

什么是 Stream?

Stream(流)是一个来自数据源的元素队列并支持聚合操作

  • 元素是特定类型的对象,形成一个队列。 Java中的Stream并不会存储元素,而是按需计算。
  • 数据源 流的来源。 可以是集合,数组,I/O channel, 产生器generator 等。
  • 聚合操作 类似SQL语句一样的操作, 比如filter, map, reduce, find, match, sorted等。

和以前的Collection操作不同, Stream操作还有两个基础的特征:

  • Pipelining: 中间操作都会返回流对象本身。 这样多个操作可以串联成一个管道, 如同流式风格(fluent style)。 这样做可以对操作进行优化, 比如延迟执行(laziness)和短路( short-circuiting)。
  • 内部迭代: 以前对集合遍历都是通过Iterator或者For-Each的方式, 显式的在集合外部进行迭代, 这叫做外部迭代。 Stream提供了内部迭代的方式, 通过访问者模式(Visitor)实现。

简单的例子

​ 在了解Java流之前,必须要先知道lambda表达式的用法,如果不了解lambda表达式,可以先看看这位大神的lambda表达式详解。

​ 先列举一些常用的Java流的方法,免得不知道自己在干嘛,当然方法不止这些,我只写了些我自己常用的。常用方法:

  • stream:返回此集合的流。这是Collection接口下的方法,继承了Collection的List和Set都能用。
  • collect:使用Collector对此流的元素进行可变缩减操作,说人话就是,流的收集器。
  • min:求出流中的最小值。
  • max:求出流中的最大值。
  • limit:截取流中的元素并返回。
  • count:统计流中的元素个数。
  • filter:对流进行过滤。
  • sort:对流中的元素进行排序。
  • distinct:返回该流不同元素组成的流,说白了就是去重。
  • map:返回由给定函数应用于此流的元素的结果组成的流。
  • concat:合并两个流,组合成新流,哈哈,是不是很眼熟。
  • reduce:Java流中的聚合函数,这算是一个核心了,像数据库里的count 、sum 、avg 、max 、min 等函数就是一种聚合操作。

​ 有一个列表,从中筛选出值大于30的元素。看看用传统的做法和Java流的做法有什么不同。

List<Integer> list = new ArrayList<>();
list.add(1);
list.add(20);
list.add(40);
list.add(100);
// 按照以前的方法
List<Integer> list1 = new LinkedList<>();
for (Integer value : list) {       if (value > 30) {          list1.add(value);     }
}
System.out.println(list1);
// 使用java流
// 详细讲解一下,后面我就偷懒了
// list.stream()就是先将集合处理一下,返回我们需要的流
// filter的方法就是这里帮我们筛选值大于30的方法
// 这里用的是lambda表达式,所以要先了解一下lambda表达式,
// filter(Predicate<? super T> predicate)方法的参数是Predicate类型,Predicate是一个函数式接口
// 当把数据筛选出来后,此时还需要把它变成List的集合,毕竟现在还只是流,不是我们要的
// 这时候就需要collect方法了,这就是我说的流收集器,其实不止可以转换成List,还有Collectors.toSet()和Collectors.toMap()
List<Integer> list2 = list.stream().filter(value -> {return value > 30;
}).collect(Collectors.toList());
System.out.println(list2);

​ 光从这个例子看,并没法很直观的感觉出Java流的好处,也没觉得好用到哪儿去,和传统方法比起来代码量也没减少多少嘛。的确,这看上去也差不多,那么下面啥也不说了,直接上实战。

实战

​ 在实际工作中,有些时候在特殊条件下,需要自己在代码里对数据进行一些处理,如果能直接从数据库查询出来直接用当然完美,但那也只是理想情况。所以,一个成熟的程序员,要学习自己处理数据。这里的@Data注解有用到lombok,不了解的可以先看看这位大神的文章lombok。

​ 这里有两个实体类,一个用户类,一个任务类,下面将列举多种情况,看看用Java流是怎么处理的(并对比一下,如果没有Java流,按照传统的做法,是该怎么做)。

@Data
public class User {/*** 用户id*/private int id;/*** 用户年龄*/private int age;/*** 用户名称*/private String name;/*** 用户电话*/private String phoneNumber;
}
@Data
public class Task {/*** 任务id*/private int id;/*** 接任务用户的用户id*/private int userId;/*** 任务描述*/private String taskContent;/*** 任务奖金*/private int bonus;
}
  1. 场景一:系统需要获取任务信息包括接任务的用户名称做一个数据展示,但不需要年龄和电话号码,需要返回的大概是这种数据。
public class TaskVo {/*** 任务id*/private int id;/*** 用户名称*/private String userName;/*** 任务描述*/private String taskContent;/*** 任务奖金*/private int bonus;
}
public class test {public static void main(String[] args) {// 先来构建数据List<User> users = Arrays.asList(new User(1, "小明", 17, "13879873219"), new User(2, "小红", 20, "13879873218"), new User(3, "小陈", 39, "13879873217"));List<Task> tasks = Arrays.asList(new Task(1, 1, "扶老奶奶过马路", 100), new Task(2, 2, "帮助程序员A找到系统bug", 100), new Task(3, 3, "给作者点个赞再走吧", 10000));// 这里将数据转换成我们需要的一个Map,以用户id为key,用户的name为valueMap<Integer, String> userMap = users.stream().collect(Collectors.toMap(User::getId, User::getName));// 使用Java流将数据处理成我们需要的样子List<TaskVo> list = tasks.stream().map(task -> {// map方法的用处我已经在前面写过了,这里是具体的用法。// 下面是构建我需要的一个TaskVo对象并返回// 当然网上有很多对象转换的方式和工具,我这里对象的属性少,这样写无所谓,正式开发可能遇到那种比较多的,所以别学我,也别问我为什么不用工具,作者不想回答你并向你扔了一只狗TaskVo vo = new TaskVo();vo.setBonus(task.getBonus());vo.setId(task.getId());vo.setTaskContent(task.getTaskContent());// 注意这里的用户名是从userMap里取的哟vo.setUserName(userMap.get(task.getId()));return vo;}).collect(Collectors.toList());// 打印出来瞧瞧list.stream().forEach(System.out::println);}
}

  1. 场景二:公司需要统计一下接了奖金1000以上(包含1000)的任务的人,能接1000以上的人都是有能力的人,所以需要重点关注,看能不能发展成公司的核心用户,并按照奖金把高的排在前面,低的排后面。
public class test {public static void main(String[] args) {// 构建数据List<User> users = Arrays.asList(new User(1, "小明", 17, "13879873219"), new User(2, "小红", 20, "13879873218"), new User(3, "小陈", 39, "13879873217"), new User(4, "小李", 25, "13879873217"));List<Task> tasks = Arrays.asList(new Task(1, 1, "给同事带杯奶茶", 1), new Task(2, 2, "帮助程序员A找到系统bug", 100), new Task(3, 3, "给作者点个赞再走吧", 1000), new Task(4, 4, "如果喜欢收藏一下再走吧", 10000));// 这里是干嘛的就不说了吧Map<Integer, String> userMap = users.stream().collect(Collectors.toMap(User::getId, User::getName));// 筛选出我们需要的人才List<String> list = tasks.stream().filter(task -> {// 筛选出金额大于等于1000的return task.getBonus() >= 1000;}).sorted((t1, t2) -> {// 排序return t2.getBonus() - t1.getBonus();}).map(task -> {// 取出用户名return userMap.get(task.getUserId());}).collect(Collectors.toList());// 做一下打印,有能力的人都是接的什么任务我就不多说了吧,你懂的list.stream().forEach(System.out::println);}
}

  1. 场景三:公司需要预计一下用户完成任务后本月需要结算给用户的任务奖金,这样的话,财务好早做准备。
public class test {public static void main(String[] args) {List<Task> tasks = Arrays.asList(new Task(1, 1, "给同事带杯奶茶", 1), new Task(2, 2, "帮助程序员A找到系统bug", 100), new Task(3, 3, "给作者点个赞再走吧", 1000), new Task(4, 4, "如果喜欢收藏一下再走吧", 10000));// 计算所有任务金额,这里就是reduce方法的作用,也就是聚合函数,就实现了类似数据库的sum函数了Integer money = tasks.stream().map(Task::getBonus).reduce((obj1, obj2) -> {// 这里的话,就可以看成reduce方法会把流中的每一个元素取出,并进行加法操作return obj1 + obj2;}).get();System.out.println(money);}
}

​ 当然reduce还有一个重载方法,有两个参数(其实还有一个重装方法,有三个参数,不过我几乎没用过,实际开发也很少用到,所以有兴趣的可以自己去了解一下)。第一个参数是作为初始值来使用的,第二个参数就和前面一个参数一样的。

​ 举个实际点的例子,公司除了要预计本月看得见的任务奖金,还要预留一部分钱,以防止某个牛人在月底突然接了任务然后以极快的速度把它给完成了,到时候不给人家结算也不好是吧。

public class test {public static void main(String[] args) {List<Task> tasks = Arrays.asList(new Task(1, 1, "给同事带杯奶茶", 1), new Task(2, 2, "帮助程序员A找到系统bug", 100), new Task(3, 3, "给作者点个赞再走吧", 1000), new Task(4, 4, "如果喜欢收藏一下再走吧", 10000));// 这里获取值的有点细微的区别,仔细看看哦Integer money = tasks.stream().map(Task::getBonus).reduce(1000, (obj1, obj2) -> {return obj1 + obj2;});System.out.println(money);}
}

​ reduce方法是很核心的方法,只要理解了它的用法,那么max、min、sum等等聚合函数的实现都可以通过reduce方法完成。

其他方法的使用就不一一列举了,本文主要记录一下使用的思路。总结一下,Java流让我们开发的时候能更方便的处理数据,并且使用的时候就感觉像数据库的SQL语句。需要注意的是,在Streams开始和结束之前,都需要避免处理null值,使用filter可以过滤掉。

Java流(Stream)相关推荐

  1. Java—— 流(Stream)、文件(File)和IO

    参考于:https://blog.csdn.net/qq_22063697/article/details/52137369  版权声明:本文为博主原创文章,转载请附上博文链接! 一.  什么是 IO ...

  2. Java 流(Stream)、文件(File)和IO -- Java ByteArrayOutputStream类

    Java ByteArrayOutputStream类 字节数组输出流在内存中创建一个字节数组缓冲区,所有发送到输出流的数据保存在该字节数组缓冲区中.创建字节数组输出流对象有以下几种方式. 下面的构造 ...

  3. Java 方法、 流(Stream)、文件(File)和IO 总结

    这里只总结几个要点. 1. 方法的命名规则 1.方法的名字的第一个单词应以小写字母作为开头,后面的单词则用大写字母开头写,不使用连接符.例如:addPerson. 2.下划线可能出现在 JUnit 测 ...

  4. Java 8 - Stream流骚操作解读2_归约操作

    文章目录 Pre 什么是归约操作 元素求和 reduce reduce如何运行的 最大值和最小值 Pre Java 8 - Stream流骚操作解读见到过的终端操作都是返回一个 boolean ( a ...

  5. Java 8 - Stream流骚操作解读

    文章目录 分类 中间操作 终端操作 使用Stream流 筛选和切片 用谓词筛选 filter 筛选各异的元素 distinct 截短流 limit 跳过元素 skip 映射 对流中每一个元素应用函数 ...

  6. [Java基础]Stream流的收集操作

    代码如下: package CollectPack;import java.util.*; import java.util.stream.Collectors; import java.util.s ...

  7. [Java基础]Stream流的常见中间操作方法

    代码如下: package StreamTest;import java.util.ArrayList;public class StreamDemo02 {public static void ma ...

  8. [Java基础]Stream流的常见生成方式

    1.Collection体系的集合可以使用默认方法stream()生成流 default Stream< E > stream() 代码如下: package StreamTest;imp ...

  9. Java 流式编程stream

    目录 什么是Stream? 怎么创建Stream? Stream的中间操作 Stream的终端操作 什么是Stream? Stream它并不是一个容器,它只是对容器的功能进行了增强,添加了很多便利的操 ...

  10. 【Java】Stream流和方法引用

    1 Stream流 1.1 Stream流优化过滤集合 传统方式 用一个循环过滤姓张的人 用一个循环过滤名字长度大于2的人 public static void main(String[] args) ...

最新文章

  1. TeaseR++:快速鲁棒的C++点云配准库介绍+英文版视频教程
  2. ZBar与ZXing使用后感觉
  3. java spring注解教程,spring注解
  4. 数字转换为字符的L受哪个参数影响
  5. 谈谈你对MVC和三层架构的理解?(月薪三万的面试题)
  6. 【蓝桥杯】基础练习 数列排序
  7. 剑指offer之61-66题解
  8. HDU5853 Jong Hyok and String(二分 + 后缀数组)
  9. ACM与Java -- 大整数类的常用函数一览表
  10. 【Java】计算二进制数中1的个数
  11. java 图文生成图片_java生成图片
  12. 卢伟冰为Redmi K50电竞版预热:相机拍照效果越来越出色
  13. SpringBoot 简单实现仿CAS单点登录系统
  14. 安卓手机怎么彻底清理手机内存_手机内存难清理?试试直接删掉这3个文件夹...
  15. 大数据技术原理与应用(第三版)林子雨教材配套实验答案---实验二 熟悉常用的hdfs操作
  16. 读取AutoCAD中的样条曲线(一)
  17. 《计算智能导论》下载
  18. 2023最新SSM计算机毕业设计选题大全(附源码+LW)之java学生综合考评系统b8vlm
  19. 苹果笔记本计算机内存不足怎么办,macbook内存不够用怎么加_苹果电脑增加内存的具体方法...
  20. logstash 导入数据,查看每秒导入的数据量及已导入数量和已导入时间

热门文章

  1. linux命令弹出移动硬盘,usb设备(移动硬盘或U盘),弹出时提示“有进程或程序占用,无法弹出”。解决办法...
  2. 单表(sqlserver不支持)、整库,支持本地和远程备份
  3. VS2019删除空白行
  4. 北海屠龙记------十三
  5. tensorflow ckpt模型转saved_model格式并进行模型预测
  6. 鸡兔同笼html语言,鸡兔同笼有哪五种方法
  7. iOS小技能:模拟鼠标点击(针对Mac)
  8. AI足球预测软件|足球大数据预测分析
  9. rufus中gpt和mrb磁盘_SSD固态硬盘用GPT还是MBR分区?
  10. Matlab中grid 的使用