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时,遇到的一些方法,该传递什么函数参数,以及返回结果是什么,就了然于胸了。

相关推荐
喵手11 分钟前
如何利用Java的Stream API提高代码的简洁度和效率?
java·后端·java ee
掘金码甲哥18 分钟前
全网最全的跨域资源共享CORS方案分析
后端
m0_4805026425 分钟前
Rust 入门 生命周期-next2 (十九)
开发语言·后端·rust
张醒言31 分钟前
Protocol Buffers 中 optional 关键字的发展史
后端·rpc·protobuf
鹿鹿的布丁1 小时前
通过Lua脚本多个网关循环外呼
后端
墨子白1 小时前
application.yml 文件必须配置哇
后端
xcya1 小时前
Java ReentrantLock 核心用法
后端
用户466537015051 小时前
如何在 IntelliJ IDEA 中可视化压缩提交到生产分支
后端·github
小楓12011 小时前
MySQL數據庫開發教學(一) 基本架構
数据库·后端·mysql
天天摸鱼的java工程师1 小时前
Java 解析 JSON 文件:八年老开发的实战总结(从业务到代码)
java·后端·面试