java8stream实战

山上一边边      2022-05-16     429

关键词:

概述

平时工作用python的机会比较多,习惯了python函数式编程的简洁和优雅。切换到java后,对于数据处理的『冗长代码』还是有点不习惯的。有幸的是,Java8版本后,引入了Lambda表达式和流的新特性,当流和Lambda表达式结合起来一起使用时,因为流申明式处理数据集合的特点,可以让代码变得简洁易读。幸福感爆棚,有没有!

本文主要列举一些stream的使用例子,并附上相应代码。

实例

先准备测试用的数据,这里简单声明了一个Person类,有名称和年龄两个属性,采用 lombok 注解方式节省了一些模板是的代码,让代码更加简洁。

    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    private static class Person {
        private String name;
        private Integer age;
    }

    private List<Person> initPersonList() {
        return Lists.newArrayList(new Person("Tom", 18),
                new Person("Ben", 22),
                new Person("Jack", 16),
                new Person("Hope", 4),
                new Person("Jane", 19),
                new Person("Hope", 16));
    }

filter

说明

  • 遍历数据并检查其中的元素是否符合要求,不符合要求的过滤掉
  • filter接受一个函数作为参数(Predicate),该函数用Lambda表达式表示,返回true or false,返回false的数据会被过滤

示例图

数据集合过Predicate方法,留下返回true的数据集合

技术图片

代码

    @Test
    public void filterTest() {
        List<Person> personList = initPersonList();
        // 过滤出年龄大于8的数据
        List<Person> result = personList.stream().filter(x -> x.getAge() > 18).collect(Collectors.toList());
        log.info(JsonUtils.toJson(result));
        // filter 链式调用实现 and
        result =
                personList.stream().filter(x -> x.getAge() > 18).filter(x -> x.getName().startsWith("J")).collect(Collectors.toList());
        log.info(JsonUtils.toJson(result));
        // 通过 Predicate 实现 or
        Predicate<Person> con1 = x -> x.getAge() > 18;
        Predicate<Person> con2 = x -> x.getName().startsWith("J");
        result =
                personList.stream().filter(con1.or(con2)).collect(Collectors.toList());
        log.info(JsonUtils.toJson(result));
    }

以上是filter的例子,可以使用链式调用实现『与』的逻辑。通过声明Predicate,并使用 or 实现『或』逻辑

map

说明

  • map生成的是个一对一映射,for的作用
  • map接收一个函数做为参数,此函数为Function,执行方法R apply(T t),因此map是一对一映射

示例图

数据集合经过map方法后生成的数据集合,数据个数保持不变,即一对一映射

技术图片

代码

@Test
    public void mapTest() {
        List<Person> personList = initPersonList();
        List<String> result = personList.stream().map(Person::getName).collect(Collectors.toList());
        log.info(JsonUtils.toJson(result));
        Set<String> nameSet =
                personList.stream().filter(x -> x.getAge() < 20).map(Person::getName).collect(Collectors.toSet());
        log.info(JsonUtils.toJson(nameSet));
    }

map比较简单,这里不赘述了,直接看代码

flatmap

说明

  • 和map不同的是,flatmap是个一对多的映射,然后把多个打平
  • flatmap接收的函数参数也是Fuction,但是还和map的入参Function相比,可以看到返回值不同。flatmap,返回的是Stream<R>,map返回的是R,这就是上面说的一对多映射

示例图

从图中也可以看到一对多映射,例如红色圆圈经过flapmap后变成了2个(一个菱形、一个方形)

技术图片

代码

    @Test
    public void flatMapTest() {
        List<Person> personList = initPersonList();
        List<String> result =
                personList.stream().flatMap(x -> Arrays.stream(x.getName().split("n"))).collect(Collectors.toList());
        log.info(JsonUtils.toJson(result));
    }

以上代码打印:["Tom","Be","Jack","Hope","Ja","e","Hope"],对每个人的姓名用字母n做了切分

reduce

说明

  • 是个多对一的映射,概念和hadoop中常用的map-reduce中的reduce相同
  • reduce接收两个参数,一个是identity(恒等值,比如累加计算中的初始累加值),另一个是BiFunction,调用方法R apply(T t, U u),即把两个值reduce为一个值

示例图

下面是1+2+3+4+5的例子,可以用reduce来解决

技术图片

代码

    @Test
    public void reduceTest() {
        Integer sum = Stream.of(1, 2, 3, 4, 5).reduce(0, Integer::sum);
        Assert.assertEquals(15, sum.intValue());
        sum = Stream.of(1, 2, 3, 4, 5).reduce(10, Integer::sum);
        Assert.assertEquals(25, sum.intValue());
        String result = Stream.of("1", "2", "3")
                .reduce("0", (x, y) -> (x + "," + y));
        log.info(result);
    }

对应示例图的代码实现,有数字求和的例子和字符串拼接的例子

collect

collect在流中生成列表,map,等常用的数据结构。常用的有toList(), toSet(), toMap()

下面代码列举了几个常用的场景

    @Test
    public void collectTest() {
        List<Person> personList = initPersonList();
        // 以name为key, 建立name-person的映射,如果key重复,后者覆盖前者
        Map<String, Person> result = personList.stream().collect(Collectors.toMap(Person::getName, x -> x,
                (x, y) -> y));
        log.info(JsonUtils.toJson(result));
        // 以name为key, 建立name-person_list的映射,即一对多
        Map<String, List<Person>> name2Persons = personList.stream().collect(Collectors.groupingBy(Person::getName));
        log.info(JsonUtils.toJson(name2Persons));
        String name = personList.stream().map(Person::getName).collect(Collectors.joining(",", "{", "}"));
        Assert.assertEquals("{Tom,Ben,Jack,Hope,Jane,Hope}", name);

        // partitioningBy will always return a map with two entries, one for where the predicate is true and one for where it is false. It is possible that both entries will have empty lists, but they will exist.
        List<Integer> integerList = Arrays.asList(3, 4, 5, 6, 7);
        Map<Boolean, List<Integer>> result1 = integerList.stream().collect(Collectors.partitioningBy(i -> i < 3));
        log.info(JsonUtils.toJson(result1));

        result1 = integerList.stream().collect(Collectors.groupingBy(i -> i < 3));
        log.info(JsonUtils.toJson(result1));
    }
  1. 建立name-person的映射,如果key重复,后者覆盖前者。Collectors.toMap的第三个参数就是BiFunction,和reduce中的一样,输入两个参数,返回一个参数。(x, y) -> y 就是(oldValue, newValue) -> oldValue,如果不加这个方法,那么当出现map的key重复,会直接抛异常
  2. 将list转化为一对多的map,可以采用Collectors.groupingBy,上述例子就是用person的name做为key,建议一对多映射关系
  3. 这里提到了groupingBypartitioningBy的区别,前者是根据某个key进行分组,后者是分类,看他们的入参就明白了,groupingBy的入参是FunctionpartitioningBy的入参是Predicate,即返回的是true/false。所以partitioningBy的key就是两类,true和false(即使存在空列表,true 和 false 两类还是会存在)

代码下载

  • Java 8 stream 实战:代码 commit,源码下载

参考文档

  • 使用 Stream API 优化代码
  • Java 8 - Stream 集合操作快速上手

java8stream排序

查看详情

java8stream流

Java8Stream流Java8关于map和flatMap的代码片段思考Java8初体验(二)Stream语法详解distinct()/*返回一个流包含不同的元素(根据equals方法判断,null值并不会报空指针异常,会保留一个null)。对于有序的流保证稳定性,保留最先出现的元... 查看详情

java8stream包

List<Student>targetStudentList=people.getStudents().stream().filter(Student->student.getId().equals(studentId)).collect(Collectors.toList());//筛选符合条件的数据并返回ListList<Long>studentIdLis 查看详情

java8stream介绍及使用

(原)stream的内容比较多,先简单看一下它的说明:Asequenceofelementssupportingsequentialandparallelaggregate*operations.Thefollowingexampleillustratesanaggregateoperationusing*{@linkStream}and{@linkIntStream}:**<pre> 查看详情

java8stream操作(代码片段)

java8stream操作//获取对象集合中某字段的数字和(不会出现空指针)int=集合对象.stream().mapToInt(集合的对象类名::某字段的get方法).sum()//向对象集合中所有对象放入同一字段值(不会出现空指针)集合对象.forEach(... 查看详情

java8stream操作(代码片段)

java8stream操作//获取对象集合中某字段的数字和(不会出现空指针)int=集合对象.stream().mapToInt(集合的对象类名::某字段的get方法).sum()//向对象集合中所有对象放入同一字段值(不会出现空指针)集合对象.forEach(... 查看详情

java8stream操作(代码片段)

java8stream操作//获取对象集合中某字段的数字和(不会出现空指针)int=集合对象.stream().mapToInt(集合的对象类名::某字段的get方法).sum()//向对象集合中所有对象放入同一字段值(不会出现空指针)集合对象.forEach(... 查看详情

java8streams多个分组依据(代码片段)

我的温度记录是这样的dt|AverageTemperature|AverageTemperatureUncertainty|City|Country|Latitude|Longitude----------+-------------------+-----------------------------+-------+--------+--------+---------1963-01-01| 查看详情

java8stream学习

 第一个StreamDemoIDEA里面写Stream有个坑   虽然java文件中没错,但是但编译的时候还是报错了,Information:java:javacTask:源发行版1.8需要目标发行版1.8 解决方法是:perferences->Build,Execution,Deployment->Compiler->JavaC 查看详情

java8stream.filter过滤集合中的数据

java8stream.filter过滤集合中的数据List<Problem>problemByExample=problemService.getProblemByExample(problemExample);List<Problem>problemList=problemByExample.stream().filter(problem->"空调制冷& 查看详情

对java8stream的简单实践

最近学习很多Java8方面的新特性,特地做了一些简单的实践和总结。importjava.util.*;importjava.util.stream.Collectors;publicclassStreamTest{staticclassUser{privateStringname;privateintage;publicStringgetName(){returnname;}publicvoids 查看详情

java8stream分组的性能对比

java8stream分组的性能对比:一般的分组虽然代码不优雅,但是效率还不错。stream分组优雅,效率却一般见这里:https://blog.csdn.net/fenglibing/article/details/80238310另外javastream处理分组后取每组最大,见这里的例子https... 查看详情

java8stream比for循环快吗

参考技术Afor是单线程顺序执行,stream可以被多核并行执行。只少不慢于for本回答被提问者采纳 查看详情

java基础java8stream相关内容的简单总结(代码片段)

文章目录【Java基础】Java8Stream相关内容的简单总结1、创建集合的几个方式2、串行和并行3、遍历4、匹配find/match5、聚合max/min/count6、收集collect(使用Collectors类)7、映射map/flatMap8、筛选filter9、排序sorted10、提取/组合【Java基础】Ja... 查看详情

java8stream学习笔记

一、什么是StreamStream是java8的新增特性,表示数据流。二、Stream的特点==不存储数据==:流是基于数据源的对象,本身不存储数据。==函数式编程==:流的操作不会修改数据源。==延迟操作==:流的很多操作如filter、map是延迟执行的... 查看详情

java8stream分组的性能对比

java8stream分组的性能对比:一般的分组虽然代码不优雅,但是效率还不错。stream分组优雅,效率却一般见这里:https://blog.csdn.net/fenglibing/article/details/80238310另外javastream处理分组后取每组最大,见这里的例子https... 查看详情

java8stream流常用方法及例子

交易员和交易的实体类的定义如下:Trader.classpublicclassTrader{privateStringname;privateStringcity;publicTrader(Stringn,Stringc){this.name=n;this.city=c;}publicStringgetName(){returnthis.name;}publicStringgetCity(){re 查看详情

利用java8stream对map排序

原文地址:https://blog.csdn.net/u013719012/article/details/82117477importjava.util.*;importjava.util.stream.Collectors;publicclassMain2{publicstaticvoidmain(String[]args){Map<Integer,String>abcMap=ne 查看详情