Java8-Stream流-实际业务常用api案例

Stream流介绍

Stream基础概念

Stream是Java8开始引入的一个流式API,位于java.util.stream包中。记住哈,这个流可不是文件流的流,它是一个序列流,可以理解成数组、列表等这种包含的元素而形成的一个流。我们先看一个简单的stream流的写法:

java 复制代码
    @Test
    public void basicStream() {
        List<PredicateDemo.People> people = new PredicateDemo().mockPeopleList();
        people.stream().filter(p -> p.getAge() > 20).map(p -> p.getId()).sorted().limit(100).collect(Collectors.toList()).forEach(System.out::println);
    }

通过上图可以看出,一个流中间进行了若干次的计算转换,然后得到结果,Stream流的特点就是:

  • Stream API提供了一套新的流式处理的抽象序列
  • Stream API支持函数编程和链式操作
  • Stream本身不存储元素,存储的是计算过程,只有获取结果的时候,才会进行计算然后得到结果。

Stream中的操作概述

stream中支持的方法有很多,我们将其按照中间操作和终端操作,分为两类如下:

实际业务中常见用法

Stream流的创建方式

Stream静态方法 list等转换

java 复制代码
Stream<Integer> integerStream = Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
integerStream.forEach(System.out::println);


List<PredicateDemo.People> people = new PredicateDemo().mockPeopleList();
Stream<PredicateDemo.People> stream = people.stream();

Stream流中间操作

过滤 filter(Predicate<? super T> predicate)

过滤掉不符合条件的元素,筛选出符合条件的元素,如下例:filter参数接收一个Predicate参数,最终筛选的元素是predicate=true的元素。

java 复制代码
    @Test
    public void filter(){
        Stream<Integer> integerStream = Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
        integerStream.filter(x -> x > 5).forEach(System.out::println); // 6 7 8 9 10
    }
限制个数 limit(long maxSize)

指定获取限制个数的元素。

java 复制代码
Stream<Integer> stream = Stream.of(2, 2, 3, 7, 8);
        // 取前3个元素
        Stream<Integer> stream2= stream.limit(3);
        stream2.forEach((x) -> {
            System.out.print(x + "\t");
        });
        // 2    2   3
跳过 skip(long n)

跳过n个元素,再获取,这里配合limit可以实现类似分页的场景。

java 复制代码
Stream<Integer> stream = Stream.of(2, 2, 3, 7, 8);
        // 跳过前3个元素
        Stream<Integer> stream2 = stream.skip(3);
        stream2.forEach((x) -> {
            System.out.print(x + "\t");
        });
        // 7 8
去重 distinct()

对元素进行去重,去重的标准是元素对象的hashCode方法和equals方法

java 复制代码
Stream<Integer> stream = Stream.of(2, 2, 3, 7, 8);
        // 去重
        Stream<Integer> stream2 = stream.distinct();
        stream2.forEach((x) -> {
            System.out.print(x + "\t");
        });
        //2 3   7   8
映射 map(Function<? super T, ? extends R> mapper)

可以看到接收的是一个函数参数,这个函数会计算每个元素,然后映射成一个新的元素。比如 People对象 取其中一个字段name,可以将List 映射成 List

java 复制代码
List<PredicateDemo.People> people = new PredicateDemo().mockPeopleList();
List<String> collect = people.stream().map(PredicateDemo.People::getName).collect(Collectors.toList());

映射 flatMap(Function<? super T, ? extends Stream<? extends R>> mapper)

简单的理解就是可以将多个流转成一个流,可以看到Function中第二个参数就是Stream。

java 复制代码
List<PredicateDemo.People> people = new PredicateDemo().mockPeopleList();
List<PredicateDemo.People> people2 = new PredicateDemo().mockPeopleList();
List<PredicateDemo.People> people3 = new PredicateDemo().mockPeopleList();
List<List<PredicateDemo.People>> listOfLists = ListUtil.of(people, people2, people3);

List<PredicateDemo.People> peopleTogether = listOfLists.stream().flatMap(List::stream).collect(Collectors.toList());
排序 sorted(Comparator<? super T> comparator)
java 复制代码
Stream<Integer> stream = Stream.of(2, 1, 7, 3, 8);
        // 自定义排序方式,将元素从大到小排序
        Stream<Integer> stream2 = stream.sorted((a, b) -> {
            return Math.negateExact(a - b);
        });
        stream2.forEach((x) -> {
            System.out.print(x + "\t");
        });
        // 8    7   3   2   1
消费 peek(Consumer<? super T> action)

peek接收的是一个Consumer函数,Consumer前面介绍过了,Consumer是消费者,是接收参数但是没有返回值,这里看下peek是怎么使用的Consumer。

java 复制代码
    @Test
    public void peek(){
        List<PredicateDemo.People> people = new PredicateDemo().mockPeopleList();

        people.stream()
                .peek(x -> System.out.println("before map: " + x.getName()))
                .collect(Collectors.toList());

        List<PredicateDemo.People> collect = people.stream()
                .peek(x -> x.setName(RandomUtil.randomString(5)))
                .collect(Collectors.toList());
    }

Stream流终端操作

全匹配 allMatch(Predicate<? super T> predicate)

通过Predicate函数去判断是否符合条件,然后每个元素否符合返回true,否则返回false。

java 复制代码
Stream<Integer> stream = Stream.of(2, 4, 5, 6, 7);
        // 判断stream中是否全是偶数
        boolean allMatch = stream.allMatch(x -> x > 1);
        System.out.println(allMatch); // true

除此之外还有以下几种匹配操作:

  • noneMatch
  • anyMatch
查找 findFirst()

返回流中第一个元素

java 复制代码
Stream<Integer> stream = Stream.of(2, 4, 5, 6, 7);
        // 返回流中第一个元素
        Optional<Integer> findFirst = stream.findFirst();
        System.out.println(findFirst.get());// 2

其他的还有:

  • findAny 返回流中任意一个元素
统计 count()

统计元素个数总数

java 复制代码
Stream<Integer> stream = Stream.of(2, 4, 5, 6, 7);
        // 返回流中元素的总个数
        long count = stream.count();
        System.out.println(count);// 5
最大值 max()

返回流中最大值

java 复制代码
Stream<Integer> stream = Stream.of(2, 4, 5, 6, 7);
        // 返回流中元素最大值
        Integer max = stream.max(Integer::compare).get();
        System.out.println(max);// 7

与之相应的还有

  • min() 返回最小值
归并 reduce(BinaryOperator accumulator)

第一次执行时,accumulator函数的第一个参数为流中的第一个元素,第二个参数为流中元素的第二个元素;第二次执行时,第一个参数为第一次函数执行的结果,第二个参数为流中的第三个元素;依次类推将元素故并计算得出结果。这个过程类似大数据map-reduce的过程,先分散再聚合。

java 复制代码
Stream<Integer> stream = Stream.of(2, 4, 5, 6, 7);
        // 实现数字累加
        Optional<Integer> reduce = stream.reduce((a, b) -> {
            return a + b;
        });
        System.out.println(reduce.get()); // 24
收集 collect
集合 toList / toSet / toMap
java 复制代码
    @Test
    public void map(){
        List<PredicateDemo.People> people = new PredicateDemo().mockPeopleList();
        List<PredicateDemo.People> collect1 = people.stream().filter(p -> p.getAge() > 18).collect(Collectors.toList());
        
        Set<PredicateDemo.People> collect2 = people.stream().filter(p -> p.getAge() > 18).collect(Collectors.toSet());
        
        Map<Integer, PredicateDemo.People> collect3 = people.stream().filter(p -> p.getAge() > 18).collect(Collectors.toMap(p -> p.getId(), p -> p));
    }
平均值 averagingInt()
java 复制代码
Stream<Integer> stream = Stream.of(1, 2, 2, 3, 4);
        Double av = stream.collect(Collectors.averagingInt((x) -> {
            return x;
        }));
        System.out.println(av); // 2.4
求和 summingInt()
java 复制代码
Stream<Integer> stream = Stream.of(1, 2, 2, 3, 4);
        Integer sum = stream.collect(Collectors.summingInt((x) -> {
            return x;
        }));
        System.out.println(sum); // 12
最大值 maxBy()
java 复制代码
Stream<Integer> stream = Stream.of(1, 2, 2, 3, 4);
        Optional<Integer> max = stream.collect(Collectors.maxBy(Integer::compare));
        System.out.println(max.get()); // 4
分组 groupingBy()

将元素进行分组。

java 复制代码
@Test
    public void groupingBy(){
        List<PredicateDemo.People> people = new PredicateDemo().mockPeopleList();
        Map<String, List<PredicateDemo.People>> collect = people.stream().filter(p -> p.getAge() > 18).collect(Collectors.groupingBy(p -> p.getSex()));
    }

分区 partitioningBy(Predicate<? super T> predicate)

参数是Predicate,所以就是将流按照是否符合条件,分成两部分。

java 复制代码
    @Test
    public void partitioningBy(){
        List<PredicateDemo.People> people = new PredicateDemo().mockPeopleList();
        Map<Boolean, List<PredicateDemo.People>> collect1 = people.stream().filter(p -> p.getAge() > 18).collect(Collectors.partitioningBy(p -> p.getLevel() > 5));
    }

总结

在Stream中经常遇到的filter、map、collect等操作方法的概念以及含义是什么,该如何使用,都通过案例和使用场景做了一一讲解。还拓展了其他的一些常见的方法,结合下面这几篇系列的文章,对函数有个熟悉的掌握,然后再使用Stream时,遇到的一些方法,该传递什么函数参数,以及返回结果是什么,就了然于胸了。

相关推荐
草捏子33 分钟前
最终一致性避坑指南:小白也能看懂的分布式系统生存法则
后端
一个public的class1 小时前
什么是 Java 泛型
java·开发语言·后端
头孢头孢2 小时前
k8s常用总结
运维·后端·k8s
TheITSea2 小时前
后端开发 SpringBoot 工程模板
spring boot·后端
Asthenia04122 小时前
编译原理中的词法分析器:从文本到符号的桥梁
后端
Asthenia04123 小时前
用RocketMQ和MyBatis实现下单-减库存-扣钱的事务一致性
后端
Pasregret3 小时前
04-深入解析 Spring 事务管理原理及源码
java·数据库·后端·spring·oracle
Micro麦可乐3 小时前
最新Spring Security实战教程(七)方法级安全控制@PreAuthorize注解的灵活运用
java·spring boot·后端·spring·intellij-idea·spring security
returnShitBoy3 小时前
Go语言中的defer关键字有什么作用?
开发语言·后端·golang
Asthenia04123 小时前
面试场景题:基于Redisson、RocketMQ和MyBatis的定时短信发送实现
后端