作为一个合格的程序员,如何让代码更简洁明了,提升编码速度尼。

今天跟着我一起来学习下java 8  stream 流的应用吧。

废话不多说,直入正题。

考虑以下业务场景,有四个人员信息,我们需要根据性别统计人员的姓名。

package com;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map; public class Test {
public static void main(String[] args) { List<Map<String, String>> list = new ArrayList<>();
Map<String, String> map = new HashMap<>();
map.put("userName", "张三");
map.put("age", "18");
map.put("sex", "男");
list.add(map); Map<String, String> map1 = new HashMap<>();
map1.put("userName", "李四");
map1.put("age", "20");
map1.put("sex", "女");
list.add(map1); Map<String, String> map2 = new HashMap<>();
map2.put("userName", "王五");
map2.put("age", "15");
map2.put("sex", "女");
list.add(map2); Map<String, String> map3 = new HashMap<>();
map3.put("userName", "若风");
map3.put("age", "23");
map3.put("sex", "男");
list.add(map3); //现在我们要根据性别统计人员的姓名
//初级写法
StringBuilder stringBuilder1 = new StringBuilder();
StringBuilder stringBuilder2 = new StringBuilder();
for (Map<String, String> item : list) {
//指向当前下标的map
System.out.println("item: " + item);
if (item.get("sex").equals("男")) {
//存性别为男的人员名称,以逗号隔开
stringBuilder1.append(item.get("userName")).append(",");
} else {
//存性别为女的人员名称,以逗号隔开
stringBuilder2.append(item.get("userName")).append(",");
}
}
//去掉最后一个逗号
String userName_nan = stringBuilder1.deleteCharAt(stringBuilder1.length() - 1).toString();
String userName_nv = stringBuilder2.deleteCharAt(stringBuilder2.length() - 1).toString();
System.out.println("userName_nan: " + userName_nan);
System.out.println("userName_nv: " + userName_nv);
}
}

打印记录如下:

是不是感觉代码写的不怎么优雅,那么我们开始换个姿势,用java8 Stream 流的方式来操作。

package com;

import org.apache.commons.lang.StringUtils;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map; public class Test {
public static void main(String[] args) { List<Map<String, String>> list = new ArrayList<>();
Map<String, String> map = new HashMap<>();
map.put("userName", "张三");
map.put("age", "18");
map.put("sex", "男");
list.add(map); Map<String, String> map1 = new HashMap<>();
map1.put("userName", "李四");
map1.put("age", "20");
map1.put("sex", "女");
list.add(map1); Map<String, String> map2 = new HashMap<>();
map2.put("userName", "王五");
map2.put("age", "15");
map2.put("sex", "女");
list.add(map2); Map<String, String> map3 = new HashMap<>();
map3.put("userName", "若风");
map3.put("age", "23");
map3.put("sex", "男");
list.add(map3); //现在我们要根据性别统计人员的姓名
//初级写法
// StringBuilder stringBuilder1 = new StringBuilder();
// StringBuilder stringBuilder2 = new StringBuilder();
// for (Map<String, String> item : list) {
// //指向当前下标的map
// System.out.println("item: " + item);
// if (item.get("sex").equals("男")) {
// //存性别为男的人员名称,以逗号隔开
// stringBuilder1.append(item.get("userName")).append(",");
// } else {
// //存性别为女的人员名称,以逗号隔开
// stringBuilder2.append(item.get("userName")).append(",");
// }
// }
// //去掉最后一个逗号
// String userName_nan = stringBuilder1.deleteCharAt(stringBuilder1.length() - 1).toString();
// String userName_nv = stringBuilder2.deleteCharAt(stringBuilder2.length() - 1).toString();
// System.out.println("userName_nan: " + userName_nan);
// System.out.println("userName_nv: " + userName_nv); //java8 stream流的写法
//先定义一个Map 用来存储我们的人员姓名
Map<String, String> userNameMap = new HashMap<>(4);
//装逼开始
//
list.stream().map(v1 -> {
//map 可以理解为new 一个新的对象,复制原来list的数据copy 到一个新的List ,用map对数据进行修改操作时,不会改变原来的对象。需要返回值!!!
//v1指向当前下标的map对象
System.out.println("v1: " + v1);
//定义一个新的map 用来存储人员的姓名和性别
Map<String, String> data = new HashMap<>();
map.put("userName", v1.get("userName"));
map.put("sex", v1.get("sex"));
return map;
}).reduce(userNameMap, (a, b) -> {
//a 可以理解为上一次循环的返回结果
//b 当前的循环对象
//取出当前循环的人员姓名 
String thisUserName = b.get("userName");
//取出当前循环的人员性别
String thisSex = b.get("sex");
if (thisSex.equals("男")) {
//取上一次循环的返回结果,第一次循环,是没有值的,第二次循环时,取得是第一次循环的返回结果,以此类推。
String val = a.get("newMenUserName");
System.out.println("val: " + val);
//第一次循环时val 是没有值的,我们赋值一个空的字符串,
// 第二次循环时,把第一次循环的返回的newUserName的值取出来,加上逗号,拼接当前下标的人员姓名
String newUserName = (StringUtils.isBlank(val) ? StringUtils.EMPTY : val + ",") + thisUserName;
//把拼接好的人员姓名添加到map 中
System.out.println("newUserName: " + newUserName);
a.put("newMenUserName", newUserName);
} else {
//取上一次循环的返回结果,第一次循环,是没有值的,第二次循环时,取得是第一次循环的返回结果,以此类推。
String val = a.get("newWomenUserName");
System.out.println("val: " + val);
//第一次循环时val 是没有值的,我们赋值一个空的字符串,
// 第二次循环时,把第一次循环的返回的newUserName的值取出来,加上逗号,拼接当前下标的人员姓名
String newUserName = (StringUtils.isBlank(val) ? StringUtils.EMPTY : val + ",") + thisUserName;
System.out.println("newUserName: " + newUserName);
//把拼接好的人员姓名添加到map 中
a.put("newWomenUserName", newUserName);
}
//返回当前循环的map
return a;
});
System.out.println("userNameMap: " + userNameMap);
System.out.println("userName_nan: " + userNameMap.get("newMenUserName"));
System.out.println("userName_nv: " + userNameMap.get("newWomenUserName")); }
}

打印的值如下:

场景二、取两个List 的交集

package com;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors; public class Test {
public static void main(String[] args) { // 业务场景2:取出两个List 的数值相同的元素(交集)
List<Integer> list1=new ArrayList<>();
list1.add(1);
list1.add(2);
list1.add(3);
list1.add(4);
List<Integer> list2=new ArrayList<>();
list2.add(2);
list2.add(4);
//交集
List<Integer> list3=new ArrayList<>();
//初级写法
for(Integer item:list1){
if(list2.contains(item)){
list3.add(item);
}
}
System.out.println("for循环的结果");
System.out.println("list3: "+list3); System.out.println("------------------------------------------------------------"); //stream 流的写法
//v1 表示当前下标的对象
//filter 过滤掉结果为false 的数据
list3=list1.stream().filter(v1->list2.contains(v1)).collect(Collectors.toList());
System.out.println("stream循环的结果");
System.out.println("list3: "+list3); //list 本身的方法
System.out.println("------------------------------------------------------------");
System.out.println("old 数据,list1:"+list1);
System.out.println("old 数据,list2:"+list2);
list1.retainAll(list2);
System.out.println("new 数据,list1"+list1);
System.out.println("new 数据,list2"+list2);
}
}

打印结果:

场景三、对一个List 排序

package com;

import java.util.*;
import java.util.stream.Collectors; public class Test {
public static void main(String[] args) { // 业务场景3:对List 排序
List<Map<String, String>> list = new ArrayList<>();
Map<String, String> map = new HashMap<>();
map.put("userName", "张三");
map.put("age", "18");
map.put("sex", "男");
list.add(map); Map<String, String> map1 = new HashMap<>();
map1.put("userName", "李四");
map1.put("age", "20");
map1.put("sex", "女");
list.add(map1); Map<String, String> map2 = new HashMap<>();
map2.put("userName", "王五");
map2.put("age", "15");
map2.put("sex", "女");
list.add(map2); Map<String, String> map3 = new HashMap<>();
map3.put("userName", "若风");
map3.put("age", "23");
map3.put("sex", "男");
list.add(map3); //stream 流的写法
System.out.println("------------------------排序前--------------------------------");
System.out.println("排序前,List :"+list);
//sorted stream排序,这里我们根据年龄和姓名进行排序
//Test::age Java 8 中我们可以通过 `::` 关键字来访问类的构造方法,对象方法,静态方法。
//装逼开始
List<Map<String, String>> list2=list.stream().sorted(Comparator.comparing(Test::age).thenComparing(Comparator.comparing(Test::userName))).collect(Collectors.toList());
System.out.println();
System.out.println("------------------------排序后--------------------------------");
System.out.println("排序后,list2 :"+list2);
}
private static String userName(Map<String,String> map){
return map.get("userName");
}
private static Integer age(Map<String,String> map){
return Integer.parseInt(map.get("age"));
}
}

打印结果:

总结:

   1、 Stream流不是一种数据结构,不保存数据,它只是在原数据集上定义了一组操作。
   2、这些操作是惰性的,即每当访问到流中的一个元素,才会在此元素上执行这一系列操作。
   3、Stream不保存数据,故每个Stream流只能使用一次。
Stream流可以分成两部分:Intermediate(中间操作)和Terminal(终止操作)。中间操作的返回结果都是Stream,故可以多个中间操作叠加;终止操作用于返回我们最终需要的数据,只能有一个终止操作!!!
一、stream流的Intermediate方法(中间操作)
1、filter(Predicate)
    将结果为false的元素过滤掉,不会改变原来对象的数据,需要显示指定返回值!!!
2、map(fun)
   把对象copy到一个新的对象中,遍历新的对象,对数据进行修改不会改变原来对象的数据,需要显示指定返回值!!!
3、flatMap(fun)
    若元素是流,将流摊平为正常元素,再进行元素转换
4、limit(n)
    保留前n个元素
5、 skip(n)
    跳过前n个元素
6、distinct()
    剔除重复元素
7、sorted(Comparator)
    将流元素按Comparator排序
8、peek(fun)
    流不变,但会把每个元素传入fun执行,可以用作调试
9、forEach()
    遍历当前对象,对数据进行修改时,会改变对象的数据,不需要显示指定返回值!!!(注意和map的区别)
二、stream流的Terminal方法(终结操作)
    约简操作
1、max(Comparator)  
        取最大值
2、min(Comparator)   
        取最小值
3、 count()         
        去和    
4、findFirst()
        返回第一个元素
5、 findAny()
        返回任意元素
6、anyMatch(Predicate)
        任意元素匹配时返回true
7、allMatch(Predicate)
        所有元素匹配时返回true
8、noneMatch(Predicate)
        没有元素匹配时返回true
9、reduce(fun)
        从流中计算某个值,接受一个二元函数作为累积器,从前两个元素开始持续应用它,累积器的中间结果作为第一个参数,流元素作为第二个参数
10、 reduce(a, fun)
        a为幺元值,作为累积器的起点
11、reduce(a, fun1, fun2)
        与二元变形类似,并发操作中,当累积器的第一个参数与第二个参数都为流元素类型时,可以对各个中间结果也应用累积器进行合并,但是当累积器的第一个参数不是流元素类型而是类型T的时候,各个中间结果也为类型T,需要fun2来将各个中间结果进行合并(参考场景一)
三、stream流的收集操作:
1、Collectors.toList()
返回一个List
2、Collectors.toSet()
返回一个集合
3、Collectors.toCollection(集合的构造器引用)
4、Collectors.joining()、Collectors.joining(delimiter)、Collectors.joining(delimiter、prefix、suffix)
字符串元素连接
5、Collectors.summarizingInt/Long/Double(ToInt/Long/DoubleFunction)
产生Int/Long/DoubleSummaryStatistics对象,它有getCount、getSum、getMax、getMin方法,注意在没有元素时,getMax和getMin返回Integer/Long/Double.MAX/MIN_VALUE
6、Collectors.toMap(fun1, fun2)/toConcurrentMap
两个fun用来产生键和值,若值为元素本身,则fun2为Function.identity()
7、Collectors.toMap(fun1, fun2, fun3)/toConcurrentMap
fun3用于解决键冲突,例如(oldValue, newValue) -> oldValue,有冲突时保留原值
8、Collectors.toMap(fun1, fun2, fun3, fun4)/toConcurrentMap
默认返回HashMap或ConcurrentHashMap,fun4可以指定返回的Map类型,为对应的构造器引元
9、Collectors.groupingBy(fun)/groupingByConcurrent(fun)
fun是分类函数,生成Map,键是fun函数结果,值是具有相同fun函数结果元素的列表
10、Collectors.partitioningBy(fun)
键是true/false,当fun是断言函数时用此方法,比groupingBy(fun)更高效
11、Collectors.groupingBy(fun1, fun2)
fun2为下游收集器,可以将列表转换成其他形式,例如toSet()、counting()、summingInt/Long/Double(fun)、maxBy(Comparator)、minBy(Comparator)、mapping(fun1, fun2)(fun1为转换函数,fun2为下游收集器)

参考: https://blog.csdn.net/lixiaobuaa/article/details/81099838

本博客仅作为学习参考
 
 

java8 新特性Stream流的应用的更多相关文章

  1. 这可能是史上最好的 Java8 新特性 Stream 流教程

    本文翻译自 https://winterbe.com/posts/2014/07/31/java8-stream-tutorial-examples/ 作者: @Winterbe 欢迎关注个人微信公众 ...

  2. Java8新特性 Stream流式思想(二)

    如何获取Stream流刚开始写博客,有一些不到位的地方,还请各位论坛大佬见谅,谢谢! package cn.com.zq.demo01.Stream.test01.Stream; import org ...

  3. Java8新特性Stream流应用示例

    Java8新特性介绍 过滤集合 List<String> newList = list.stream().filter(item -> item != null).collect(C ...

  4. Java8新特性 Stream流式思想(一)

    遍历及过滤集合中的元素使用传统方式遍历及过滤集合中的元素package cn.com.zq.demo01.Stream.test01.Stream; import java.util.ArrayLis ...

  5. Java8新特性 Stream流式思想(三)

    Stream接口中的常用方法 forEach()方法package cn.com.cqucc.demo02.StreamMethods.Test02.StreamMethods; import jav ...

  6. Java8 新特性 —— Stream 流式编程

    本文部分摘自 On Java 8 流概述 集合优化了对象的存储,大多数情况下,我们将对象存储在集合是为了处理他们.使用流可以帮助我们处理对象,无需迭代集合中的元素,即可直接提取和操作元素,并添加了很多 ...

  7. Java8新特性——stream流

    一.基本API初探 package java8.stream; import java.util.Arrays; import java.util.IntSummaryStatistics; impo ...

  8. java8 新特性 Stream流 分组 排序 过滤 多条件去重

    private static List<User> list = new ArrayList<User>(); public static void main(String[] ...

  9. Java8 新特性 Stream 无状态中间操作

    无状态中间操作 Java8 新特性 Stream 练习实例 中间无状态操作,可以在单个对单个的数据进行处理.比如:filter(过滤)一个元素的时候,也可以判断,比如map(映射)... 过滤 fil ...

随机推荐

  1. [linux] [nginx] 一键安装web环境全攻略phpstudy版,超详细!

    找到运行中的服务器(实例). 打开这个主要是看它的IP,是公网ip,公网ip,公网ip,重要的事情说三遍. 接下来我们可以不用在阿里云上操作了,直接用客户端操作,这两个客户端就是Xshell 5和Xf ...

  2. Java中Random类

    Random:产生随机数的类 构造方法: public Random();没有给种子,用的是默认种子,是当前时间的毫秒值. public Random(long seed);给出指定的种子 //给定种 ...

  3. 每天都在用,但你知道 Tomcat 的线程池有多努力吗?

    这是why的第 45 篇原创文章.说点不一样的线程池执行策略和线程拒绝策略,探讨怎么让线程池先用完最大线程池再把任务放到队列中. 荒腔走板 大家好,我是 why,一个四川程序猿,成都好男人. 先是本号 ...

  4. wincache 与 zend guard 的冲突

    ZendLoader.dll 与wincache.dll  同时开启 问题分析:zend与wincache冲突 解决方法: 关掉wincache: 在php.ini中的 extension=php_w ...

  5. 2019-2020-1 20199310《Linux内核原理与分析》第三周作业

    1.问题描述 计算机的3大法宝是存储程序计算机,函数调用堆栈和中断机制,存储程序计算机已经在上一个博客中进行具体描述,本文将在剩下两方面出发对操作系统是如何工作的进行学习和探讨. 2.解决过程 2.1 ...

  6. 【Linux题目】第六关

    [定时任务规则] 1. 如果在某用户的crontab文件中有以下记录,该行中的命令多久执行一次(RHCE考试题)?( ) 30 4 * * 3 mycmd A. 每小时. B. 每周. C. 每年三月 ...

  7. 【linux三剑客】grep命令

    grep, egrep, fgrep - print lines matching a pattern grep 命令用于查找文件里符合条件的字符串. grep 指令用于查找内容包含指定的范本样式的文 ...

  8. CSS开发技巧(三):图片点击缩放

    前言  利用CSS实现图片的点击缩放是一个很值得研究的效果.在某些业务需求场景中,我们可能并没有足够的空间展示过大的图片,这就需要限制图片容器的宽度和高度.然而图片限制了宽度,一些图片的细节便又无法看 ...

  9. Redis(三):多机数据库的实现

    复制 在Redis中,用户可以通过SLAVEOF命令或是slaveof选项设置服务器的主从关系,从(SLAVE)服务器会复制主(Master)服务器. 旧版复制功能实现(2.8以前) 旧版复制功能主要 ...

  10. 自定义Bootstrap样式弹出框

    最近做的一些功能需要用到Bootstrap,然而原来的系统并没有引入Bootstrap,为了新写的控件能够应用于老的页面,又不需要在老的页面上引入全套的Bootstrap文件决定写一个模仿Bootst ...