Java8 StreamAPI使用方法

StreamApi简述

Java 8 中的 Stream 是对集合(Collection)对象功能的增强,它专注于对集合对象进行各种非常便利、高效的聚合操作(aggregate operation),或者大批量数据操作 (bulk data operation)。Stream API 借助于同样新出现的 Lambda 表达式,极大的提高编程效率和程序可读性。同时它提供串行和并行两种模式进行汇聚操作,并发模式能够充分利用多核处理器的优势,使用 fork/join 并行方式来拆分任务和加速处理过程。通常编写并行代码很难而且容易出错, 但使用 Stream API 无需编写一行多线程的代码,就可以很方便地写出高性能的并发程序。

流的执行过程

元素流在管道中经过中间操作(intermediate operation)的处理,最后由最终操作(terminal operation)得到前面处理的结果。
stream 流的执行过程

样例

对于一个学生的列表,先过滤体育不及格的学生,再对其三门成绩进行权重相加得到总分,根据总分进行排序,并取到前三名成绩的信息。

用原有for循环的方法写,可能需要写很长的一段代码。

stream方式:

        List<Student> studentList1 = studentList.stream()
                                 .filter(t -> t.getSport() < 60)
                                 .peek(t -> t.setTotalScore(t.getMath()*1.2+t.getChinese()+t.getEnglish()*0.8))
                                 .sorted(Comparator.comparing(Student::getTotalScore).reversed())
                                 .limit(10)
                                 .collect(Collectors.toList());

流的来源

集合,数组,I/O channel, 产生器generator 等

流的操作类型分为两种:

  1. Intermediate(中间操作):一个流可以后面跟随零个或多个 intermediate 操作。其目的主要是打开流,做出某种程度的数据映射/过滤,然后返回一个新的流,交给下一个操作使用。这类操作都是惰性化的(lazy),就是说,仅仅调用到这类方法,并没有真正开始流的遍历。
  2. Terminal(终止操作):一个流只能有一个 terminal 操作,当这个操作执行后,流就被使用“光”了,无法再被操作。所以这必定是流的最后一个操作。Terminal 操作的执行,才会真正开始流的遍历

常用中间操作

map

转换操作,将A类型转换为B类型。
1对1映射,一个元素转为另一个元素

如:根据学生信息列表获取所有学生id

 List<Integer> idList = studentList.stream().map(Student::getId).collect(Collectors.toList());

flatmap

拍平操作 把 Stream 中的层级结构扁平化,就是将最底层元素抽出来放到一起
一对多映射 一个元素转为多个元素

// 对数组中所有字母去重。
 String[] words = new String[]{"Hello","World"};
List<String> wordsSplit = Arrays.stream(words)
                               .map(word -> word.split(""))
                               .flatMap(Arrays::stream)
                               .distinct()
                               .collect(toList());

结果为: [“H”,“e”,“l”,“o”,“W”,“r”,“d”]

limit

limit 方法用于获取指定数量的流
见样例

sorted

sorted 方法用于对元素排序,前提是实现Comparable接口,当然也可以自定义比较器。
见样例

filter

过滤,用于通过设置的条件过滤出元素
见样例

distinct

去除重复的元素,底层使用了equals方法。

如对id去重

 List<Integer> idListDistinct = idList.stream().distinct().collect(Collectors.toList());

peek

对选择的元素执行操作,如:读取、编辑修改等。并返回新的stream
见样例

skip

跳过前面的几个元素

        List<Integer> skipIdList = idList.stream().skip(10).collect(toList());

常用终止操作

collect

收集操作,将所有数据收集起来。官方的提供的Collectors 提供了非常多收集器,可以说Stream 的核心在于Collectors

如:
其他样例最基本的 转为list

根据字段分组

        //根据所在城市分组
        Map<String,List<Student>> cityMap = studentList.stream().collect(groupingBy(Student :: getCity));

根据是否满足某一条件分组

        //根据语文是否>90分组
        Map<Boolean,List<Student>> chineseGood = studentList.stream().collect(partitioningBy(t -> t.getChinese() > 90));
        System.out.println("语文分数>90的人数为: " + chineseGood.get(true).size());
        System.out.println("语文分数<90的人数为: " + chineseGood.get(false).size());

从对象中取出特点属性组成map

//构建成 学号->总分map,排除重复值
        Map<Integer,Double> studentMap = topStudentList.stream().collect(Collectors.toMap(Student :: getId,Student :: getTotalScore,(key1,key2) -> key2));

将list转化为string,并可指定分隔符,前缀后缀

        //取学生名字拼接为字符串,以,分割,前缀为[,后缀为]
        String nameStr = studentList.stream().map(Student::getName).collect(Collectors.joining(",","[","]"));

转化为其他collection类型:

转为为 stack及set:

        //id转为栈
        Stack idStack = idList.stream().collect(Collectors.toCollection(Stack :: new));
        //id转为set
        Set idSet = idList.stream().collect(Collectors.toSet());

其他~~

count

统计操作,统计最终的数据个数

        //体育不及格的人数
        Long count = studentList.stream().filter(t -> t.getSport() < 60).count();

findFirst、findAny

查找操作,查找第一个、查找任何一个 返回的类型为Optional
如果是数据较少,串行地情况下,一般会返回第一个结果,如果是并行的情况,那就不能确保是第一个

        //随机获取一个学生元素
        Optional<Student> studentOptionalRandom = studentList.stream().findAny();

        //获取第一个学生元素
        Optional<Student> studentOptionalFirst = studentList.stream().findFirst();

noneMatch、allMatch、anyMatch

匹配操作,数据流中是否存在符合条件的元素 返回值为bool 值。

        //是否有学生语文>95分
        Boolean anyChineseBest = studentList.stream().anyMatch(t -> t.getChinese() > 95);
        //是否所有学生体育都及格
        Boolean allSportPass = studentList.stream().allMatch(t -> t.getSport() >= 60);
        //是否没有学生体育不及格
        Boolean noneSportFail = studentList.stream().noneMatch(t -> t.getSport() < 60);

min、max

最值操作,需要自定义比较器,返回数据流中最大最小的值

//体育最好的学生
        Optional<Student> maxSportStudent = studentList.stream().max(Comparator.comparing(Student::getSport));
        //体育最差的学生
        Optional<Student> minSportStudent = studentList.stream().min(Comparator.comparing(Student::getSport));

reduce

规约操作,将整个数据流的值规约为一个值,count、min、max底层就是使用reduce。它提供一个起始值(种子),然后依照运算规则(BinaryOperator),和前面 Stream 的第一个、第二个、第 n 个元素组合。

//获取前十名的总分之和
        Double sumTotalCounnt = topStudentList.stream().map(Student::getTotalScore).reduce(0.0,Double :: sum);

forEach、forEachOrdered

遍历操作

//打印每个学生的姓名及城市
        studentList.forEach(t -> System.out.println("name:" + t.getName() + " city:" + t.getCity()));

并行parallel

parallelStream 是流并行处理程序的代替方法。在生成流的时候使用 parallelStream()进行流的获取

底层使用fork/join 框架进行处理

数据量较少时,for循环,串行流,并行流性能差异不大。
数据量特别多时,并行流可能发生阻塞,for循环的效率更好。
如何选用请跟进业务实际情况判断。

调试插件 Java Stream Debugger

使用idea插件可查看中间操作的结果
在stream中打个断点,使用debug模式调试
Java Stream Debugger调试断点

单步模式:
Java Stream Debugger 单步模式

全局概览模式:
Java Stream Debugger 使用

测试源码

Student类


import lombok.Getter;
import lombok.Setter;

/**
 * @date: 2020-05-13 14:39
 * @author: bufang
 * @description:
 */
@Setter
@Getter
public class Student {

    private Integer id;

    private String name;

    private Double totalScore;

    private Double math;

    private Double chinese;

    private Double sport;

    private Double english;

    private String city;
}

StreamApiTest


import com.alibaba.fastjson.JSON;
import com.google.common.collect.Lists;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.Stack;
import java.util.stream.Collectors;
import static java.util.stream.Collectors.groupingBy;
import static java.util.stream.Collectors.partitioningBy;
import static java.util.stream.Collectors.toList;

/**
 * @date: 2020-05-13 11:22
 * @author: bufang
 * @description:java8 streamApi 测试类
 */
public class StreamApiTest {

    private static List<Student> studentList = Lists.newArrayList();

    /**
     * 测试列表初始化
     * 成绩区间 50-100
     * 来源城市4个
     */
    private static void initList(){
        List<String> cities = Lists.newArrayList();
        cities.add("杭州");
        cities.add("湖州");
        cities.add("其他");
        cities.add("宁波");
        for (int i =0 ; i<50 ; i++){
            Student student = new Student();
            student.setId(i);
            student.setName("小" + i);
            student.setSport((Math.random( )*50+50));
            student.setCity(cities.get(i%4));

            student.setMath((Math.random( )*50+50));
            student.setEnglish((Math.random( )*50+50));
            student.setChinese((Math.random( )*50+50));
            studentList.add(student);
            studentList.add(student);

        }
    }








    public static void main(String[] args) {
        initList();
        //对于一个学生的列表,先过滤体育不及格的学生,再对其三门成绩进行权重相加得到总分,根据总分进行排序,并取到前十名成绩的信息。
        //System.out.println("开始计时 当前时间为 " + DateUtil.getFormantDate(new Date(),DATA_FORMANT15));
        List<Student> topStudentList = studentList.stream()
                                 .filter(t -> t.getSport() < 60)
                                 .peek(t -> t.setTotalScore(t.getMath()*1.2+t.getChinese()+t.getEnglish()*0.8))
                                 .sorted(Comparator.comparing(Student::getTotalScore).reversed())
                                 .limit(10)
                                 .collect(toList());

        //List<Student> topStudentList = studentList.parallelStream()
        //                                        .filter(t -> t.getSport() < 60)
        //                                        .peek(t -> t.setTotalScore(t.getMath()*1.2+t.getChinese()+t.getEnglish()*0.8))
        //                                        .sorted(Comparator.comparing(Student::getTotalScore).reversed())
        //                                        .limit(10)
        //                                        .collect(Collectors.toList());
        //System.out.println("结束计时 当前时间为 " + DateUtil.getFormantDate(new Date(),DATA_FORMANT15));
        //解析json不会进行引用显示
        //System.out.println("前十名学生对象为: " + JSON.toJSONString(topStudentList, SerializerFeature.DisableCircularReferenceDetect));

        System.out.println("前十名学生对象为: " + JSON.toJSONString(topStudentList));


        //获取前十名的总分之和
        Double sumTotalCounnt = topStudentList.stream().map(Student::getTotalScore).reduce(0.0,Double :: sum);
        System.out.println("\n前十名学生总分之和为: " + sumTotalCounnt);

        //构建成 学号->总分map
        Map<Integer,Double> studentMap = topStudentList.stream().collect(Collectors.toMap(Student :: getId,Student :: getTotalScore,(key1,key2) -> key2));
        System.out.println("\n前十名id及总分为: " + JSON.toJSONString(studentMap));

        //根据所在城市分组
        Map<String,List<Student>> cityMap = studentList.stream().collect(groupingBy(Student :: getCity));
        System.out.println("\n根据城市分组后map为: " + JSON.toJSONString(cityMap));

        //根据语文是否>90分组
        Map<Boolean,List<Student>> chineseGood = studentList.stream().collect(partitioningBy(t -> t.getChinese() > 90));
        System.out.println("\n语文分数>90的人数为: " + chineseGood.get(true).size());
        System.out.println("\n语文分数<90的人数为: " + chineseGood.get(false).size());



        //获取所有学生id
        List<Integer> idList = studentList.stream().map(Student::getId).collect(toList());
        System.out.println("\n所有学生id为: " + JSON.toJSONString(idList));

        //id去重
        List<Integer> idListDistinct = idList.stream().distinct().collect(toList());
        System.out.println("\n去重后学生id为: " + JSON.toJSONString(idListDistinct));

        //跳过前10个学生id
        List<Integer> skipIdList = idList.stream().skip(10).collect(toList());
        System.out.println("\n第10个之后的id为: " + JSON.toJSONString(skipIdList));



        //flatmap
        String[] words = new String[]{"Hello","World"};
        List<String> wordsSplit = Arrays.stream(words)
                               .map(word -> word.split(""))
                               .flatMap(Arrays::stream)
                               .distinct()
                               .collect(toList());

        System.out.println("\nflatMap 后" + JSON.toJSONString(wordsSplit));

        //取学生名字拼接为字符串,以,分割,前缀为[,后缀为]
        String nameStr = studentList.stream().map(Student::getName).collect(Collectors.joining(",","[","]"));
        System.out.println("\n所有学生姓名为 " + nameStr);

        //id转为栈
        Stack idStack = idList.stream().collect(Collectors.toCollection(Stack :: new));
        System.out.println("\nidStack " + JSON.toJSONString(idStack));
        //id转为set
        Set idSet = idList.stream().collect(Collectors.toSet());
        System.out.println("\nidSet " + JSON.toJSONString(idSet));

        //体育不及格的人数
        Long count = studentList.stream().filter(t -> t.getSport() < 60).count();
        System.out.println("\n体育不及格人数为: " + count);

        //随机获取一个学生元素
        Optional<Student> studentOptionalRandom = studentList.parallelStream().findAny();
        System.out.println("\n随机获取到的学生姓名为 : " + studentOptionalRandom.get().getName());

        //获取第一个学生元素
        Optional<Student> studentOptionalFirst = studentList.stream().findFirst();
        System.out.println("\n第一个学生姓名为 : " + studentOptionalFirst.get().getName());

        //是否有学生语文>95分
        Boolean anyChineseBest = studentList.stream().anyMatch(t -> t.getChinese() > 95);
        //是否所有学生体育都及格
        Boolean allSportPass = studentList.stream().allMatch(t -> t.getSport() >= 60);
        //是否没有学生体育不及格
        Boolean noneSportFail = studentList.stream().noneMatch(t -> t.getSport() < 60);
        System.out.println("\n是否有学生语文>95 : " + anyChineseBest);
        System.out.println("\n是否所有学生体育都及格 : " + allSportPass);
        System.out.println("\n是否没有学生体育不及格 : " + noneSportFail);

        //体育最好的学生
        Optional<Student> maxSportStudent = studentList.stream().max(Comparator.comparing(Student::getSport));
        //体育最差的学生
        Optional<Student> minSportStudent = studentList.stream().min(Comparator.comparing(Student::getSport));
        System.out.println("\n体育最好的学生为 : " + JSON.toJSONString(maxSportStudent));
        System.out.println("\n体育最差的学生为 : " + JSON.toJSONString(minSportStudent));

        //打印每个学生的姓名及城市
        studentList.forEach(t -> System.out.println("name:" + t.getName() + " city:" + t.getCity()));





    }

}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值