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

相关推荐
过客猫20221 小时前
使用 deepseek实现 go语言,读取文本文件的功能,要求支持 ascii,utf-8 等多种格式自适应
开发语言·后端·golang
刘立军2 小时前
本地大模型编程实战(20)用langgraph和智能体实现RAG(Retrieval Augmented Generation,检索增强生成)(4)
人工智能·后端·llm
jingwang-cs2 小时前
内外网文件传输 安全、可控、便捷的跨网数据传输方案
人工智能·后端·安全
Biehmltym3 小时前
【SpringMVC】概述 SSM:Spring + SpringMVC + Mybats
java·后端·spring
qw9493 小时前
SpringMVC
java·后端
customer084 小时前
【开源免费】基于SpringBoot+Vue.JS医疗报销系统(JAVA毕业设计)
java·vue.js·spring boot·后端·开源
B站计算机毕业设计超人4 小时前
计算机毕业设计SpringBoot+Vue.jst房屋租赁系统(源码+LW文档+PPT+讲解)
vue.js·spring boot·后端·eclipse·intellij-idea·mybatis·课程设计
m0_748248655 小时前
SpringBoot整合easy-es
spring boot·后端·elasticsearch
一个热爱生活的普通人5 小时前
golang的切片(Slice)底层实现解析
后端·go