首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >用好强大的Stream

用好强大的Stream

作者头像
Java识堂
发布于 2020-09-03 08:29:00
发布于 2020-09-03 08:29:00
58500
代码可运行
举报
文章被收录于专栏:Java识堂Java识堂
运行总次数:0
代码可运行

函数式接口

众所周知,Java8提供了很多新的特性,Lambda表达式,函数式接口,Optional,新的日期类api。今天简单聊一下Stream的前世今生。

Lambda表达式我们现在已经用的很多了,而函数式接口则是为了支持Lambda表达式,Java8提供了很多内置的函数式接口,如Runnable,Comparator等是从原有的API升级来的,而有些是Java8新增的,如Consumer等。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@FunctionalInterface
public interface Runnable {
    public abstract void run();
}

类上有注解@FunctionalInterface就可以认为这是一个函数式接口,可以用在Lambda表达式中。Lambda表达式极大的简化了我们的编程

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// jdk1.8之前
new Thread(new Runnable() {
    @Override
    public void run() {
        System.out.println("yes");
    }
}).start();

// jdk1.8及以后
new Thread(() -> System.out.println("yes")).start();

为了方便我们使用Lambda表达式,Java8提供了一些内置的函数式接口

函数式接口

方法

用途

Consumer<T> 消费型接口

void accept(T t)

输入参数为T,没有返回

Supplier供给型接口

T get()

返回R

Function<T, R> 函数型接口

R apply(T t)

输入参数为T,返回为R

Predicate判断型接口

boolean test(T t)

对象是否满足条件,true为满足,false为不满足

Java8为什么要新提供这些函数式接口呢?

我举个例子你就明白了。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Data
@AllArgsConstructor
public class Person {
    private String name;
    private int age;
    private int salary;
}

员工对象为Person,此时老板发话了,给我找出年龄大于20的员工把,于是就有了下面的方法

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public List<Person> filterByAge(List<Person> personList) {
    List<Person> resultList = Lists.newArrayList();
    for (Person person : personList) {
        if (person.getAge() > 20) {
            resultList.add(person);
        }
    }
    return resultList;
}

干的不错,再给我找一下工资大于2000的员工把。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public List<Person> filterBySalary(List<Person> personList) {
    List<Person> resultList = Lists.newArrayList();
    for (Person person : personList) {
        if (person.getSalary() > 2000) {
            resultList.add(person);
        }
    }
    return resultList;
}

再给我找一下,,, 老板等等,我需要优化一下这个实现。

你发现问题了吗?2个方法只有判断条件不同,其余的部分一模一样。而且可扩展性太差,该怎么优化呢?

额,我们可以定义一个如下的接口,判断的逻辑让接口的实现类去实现

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public interface Predicate<T> {
    boolean test(T t);
}
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public List<Person> filter(List<Person> personList, Predicate<Person> predicate) {
    List<Person> resultList = Lists.newArrayList();
    for (Person person : personList) {
        if (predicate.test(person)) {
            resultList.add(person);
        }
    }
    return resultList;
}

上面的需求就可以用如下几行代码实现。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
List<Person> filterByAgeList = filter(list, p -> p.getAge() > 20);
List<Person> filterBySalaryList = filter(list, p -> p.getSalary() > 2000);

此时老板再加筛选需求也不怕了。

等等,我们定义的Predicate接口和Java8内置的函数式接口好像。哈哈,基本上一模一样,因为类似的场景很多,所以Java8帮我们定义了一系列的接口。省的我们自己定义

函数式接口的使用

函数式接口

方法

用途

Consumer<T> 消费型接口

void accept(T t)

输入参数为T,没有返回

Supplier供给型接口

T get()

返回R

Function<T, R> 函数型接口

R apply(T t)

输入参数为T,返回为R

Predicate判断型接口

boolean test(T t)

对象是否满足条件,true为满足,false为不满足

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Test
public void testCase1() {
    // 10
    consumeTask(10, (m) -> System.out.println(m));
}

public void consumeTask(int num, Consumer<Integer> consumer) {
    consumer.accept(num);
}

@Test
public void testCase2() {
    // AAA
    System.out.println(strHandler("aaa", (str) -> str.toUpperCase()));
}

public String strHandler(String str, Function<String, String> function) {
    return function.apply(str);
}

当然,为了方便我们的使用,还有很多其他的内置接口,看入参和返回值就能知道接口的作用

函数式接口

方法

BiFunction<T, U, R>

R apply(T t, U u)

BiConsumer<T, U>

void accept(T t, U u)

ToIntFunction

int applyAsInt(T value)

IntFunction

R apply(int value)

Stream介绍

在Java8之前,如果我们想对集合进行操作还是比较麻烦的。Java8设计了Stream API来简化对集合的操作,Stream API的设计基于函数式编程和lambda表达式,行云流水似的编程方式给人带来新的体验。

Stream操作分为如下三个步骤

  1. 创建Stream:从数据源,例如集合,数组中获取一个流
  2. 中间操作:对数据进行处理
  3. 终止操作:执行中间操作,并产生结果。一般返回void或一个非流的结果

注意当不执行终止操作的时候,中间操作不会执行

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
List<Integer> dataList = Arrays.asList(1, 2, 3, 4);

// 没有输出
dataList.stream().map(x -> {
    System.out.println(x);
    return x;});
    
// 输出 1 2 3 4
// 正常是换行,我这用空格代替了,下同
dataList = dataList.stream().map(x -> {
    System.out.println(x);
    return x;
}).collect(Collectors.toList());

创建Stream

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// 1. Collection集合的stream()或者parallelStream()
List<String> list = Lists.newArrayList();
Stream<String> stream1 = list.stream();

// 2. 调用Arrays.stream(T[] array)静态方法
Integer[] array = {1, 2, 3};
Stream<Integer> stream2 = Arrays.stream(array);

// 3. 调用Stream.of(T... values)静态方法
Stream<String> stream3 = Stream.of("aa", "bb", "cc");

// 4. 调用Stream.iterate(final T seed, final UnaryOperator<T> f),创建无限流
// (x) -> x + 2 为函数式接口,传入x返回x+2,0为最开始的值
Stream<Integer> stream4 = Stream.iterate(0, (x) -> x + 2);
// 一直输出 0 2 4 6 8 10 12 ...
stream4.forEach(System.out::println);

// 5. 调用调用Stream.generate(),创建无限流
Stream<Integer> stream5 = Stream.generate(() -> 10);
// 一直输出10,你可以用Random等类随机生成哈
stream5.forEach(System.out::println);

中间操作

筛选与切片

函数名

解释

filter

从流中排除某些元素

limit

使元素不超过指定数量

skip

跳过前n个元素,如果流中元素不超过n个,则返回一个空流

distinct

通过hashCode()和equals()去除重复元素

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
List<Integer> list = Arrays.asList(1, 2, 3, 4);
// 1 3
list.stream().filter(x -> x % 2 == 1).forEach(System.out::println);
// 3 4
list.stream().skip(2).forEach(System.out::println);

看一下filter方法和forEach方法的定义

Stream.java

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
Stream<T> filter(Predicate<? super T> predicate);
void forEach(Consumer<? super T> action);

这不就是我门上面介绍的函数式接口吗?很多方法的入参其实就是一个函数式接口

映射

函数名

解释

map

接收一个函数作为参数,该函数被应用到每个元素上,并将其映射成一个新的元素

flatMap

接受一个函数作为参数,将流中的每一个值都转换成另一个流,然后将所有流连接成一个流

先看这2个方法的定义

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<R> Stream<R> map(Function<? super T, ? extends R> mapper);
<R> Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper);

map方法的入参和返回值可以为任意值 flatMap方法的入参为任意值,返回值必须为Stream

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
List<String> list = Arrays.asList("abcd", "efgh");

// [Ljava.lang.String;@7b3300e5 [Ljava.lang.String;@2e5c649
list.stream().map(x -> x.split("")).forEach(System.out::println);

// a b c d e f g h
list.stream().flatMap(x -> Arrays.stream(x.split(""))).forEach(System.out::println);

解释一下这个输出,x.split("")后为数组,所以第一个输出的为数组的地址 第二个x.split("")后为数组,然后将多个数组转为多个流,将多个流合并后输出

排序

函数名

解释

sorted()

自然排序,通过Comparable接口定义的规则来排序

sorted(Comparator)

定制排序,通过Comparator接口定义的规则来排序

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
List<String> list = Arrays.asList("b", "a", "c");
// a b c
list.stream().sorted().forEach(System.out::println);
// c b a
list.stream().sorted((x, y) -> y.compareTo(x)).forEach(System.out::println);

终止操作

查找与匹配

函数名

解释

allMatch

是否匹配所有元素

anyMatch

是否至少匹配一个元素

noneMatch

是否没有匹配所有元素

findFirst

返回第一个元素

findAny

返回当前流中的任意元素

count

返回当前流中元素总个数

max

返回流中最大值

min

返回流中最小值

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
List<Integer> list = Arrays.asList(1, 2, 3, 4);
// false
// 当list都为1时才会返回true
System.out.println(list.stream().allMatch(num -> num.equals(1)));
// true
System.out.println(list.stream().anyMatch(num -> num.equals(1)));
// 4
System.out.println(list.stream().max((x, y) -> x.compareTo(y)).get());
归约

函数名

解释

reduce

归约,将流中元素反复结合起来得到一个值

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
List<Integer> list = Arrays.asList(1, 2, 3, 4);
int sum = list.stream().reduce(0, (x, y) -> x + y);
// 10
// 初始值为0,执行过程为
// x = 0 y = 1
// x = 1 y = 2
// x = 3 y = 4 ...
// 10
// 10
 System.out.println(sum);
收集

用collect方法来进行收集,方法定义如下

Stream.java

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<R> R collect(Supplier<R> supplier,
              BiConsumer<R, ? super T> accumulator,
              BiConsumer<R, R> combiner);
              
<R, A> R collect(Collector<? super T, A, R> collector);

当然我一般不自己实现这个接口,可以直接用Collectors工具类

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Data
@AllArgsConstructor
public class Student {
    private String name;
    private int age;
}
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
List<Student> studentList = Arrays.asList(new Student("张三", 30),
        new Student("李四", 20),
        new Student("王五", 20));
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
List<String> nameList = studentList.stream().map(Student::getName).collect(Collectors.toList());
// [张三, 李四, 王五]
System.out.println(nameList);

Set<Integer> ageSet = studentList.stream().map(Student::getAge).collect(Collectors.toSet());
// [20, 30]
System.out.println(ageSet);

LinkedHashSet<Integer> linkedHashSet =
        studentList.stream().map(Student::getAge).collect(Collectors.toCollection(LinkedHashSet::new));
// [30, 20]
System.out.println(linkedHashSet);
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// 总数
long count = studentList.stream().collect(Collectors.counting());
// 3
System.out.println(count);

// 平均值
double ageAvg = studentList.stream().collect(Collectors.averagingDouble(Student::getAge));
// 23.3
System.out.println(ageAvg);

// 总和
int totalAge = studentList.stream().collect(Collectors.summingInt(Student::getAge));
// 70
System.out.println(totalAge);

// 最大值
Optional<Student> student = studentList.stream().collect(Collectors.maxBy((x, y) -> x.getAge() - y.getAge()));
// Student(name=张三, age=30)
System.out.println(student.get());

// 按照年龄分组
// 还可以多级分组,按照年龄分组后,再按照其他条件分组,不再演示
Map<Integer, List<Student>> listMap = studentList.stream().collect(Collectors.groupingBy(Student::getAge));
// {20=[StreamDemo.Student(name=李四, age=20), StreamDemo.Student(name=王五, age=20)], 30=[StreamDemo.Student(name=张三, age=30)]}
System.out.println(listMap);

一些使用Demo

枚举值参数校验

项目中有很多单选项需要定义相关的枚举值,前端传入后需要校验这些值是否在枚举范围内

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public enum MSG_TYPE {

    IMAGE((byte) 0, "图片"),
    TEXT((byte) 1, "文本");

    public final byte value;
    public final String name;

    MSG_TYPE(byte value, String name) {
        this.value = value;
        this.name = name;
    }
}
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// 模拟前端传入的参数为1
boolean isExist = Arrays.stream(MSG_TYPE.values()).anyMatch(v -> v.value == 1);
// true
System.out.println(isExist);
isExist = Arrays.stream(MSG_TYPE.values()).anyMatch(v -> v.value == 5);
// false
System.out.println(isExist);

调用远程服务前存对应关系

根据学生姓名获取学生的其他信息

  1. 先存学生姓名->学生的映射关系为nameMap
  2. 通过学生姓名调用其他服务批量获取学生信息
  3. 从nemeMap中根据其他服务返回的学生姓名拿到Student,然后填充信息到Student
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
List<Student> studentList = Lists.newArrayList();
for (int i = 0; i < 3; i++) {
    Student student = Student.builder().name("学生" + i).age(i).build();
    studentList.add(student);
}
// {学生0=Student(name=学生0, age=0), 学生2=Student(name=学生2, age=2), 学生1=Student(name=学生1, age=1)}
Map<String, Student> studentMap = studentList.stream().collect(Collectors.toMap(Student::getName, student -> student));
System.out.println(studentMap);

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-09-01,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Java识堂 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
Java 8 stream的使用示例
Stream 是 Java8 中处理集合的关键抽象概念,它可以指定你希望对集合进行的操作,可以执行非常复杂的查找、过滤和映射数据等操作。使用Stream API 对集合数据进行操作,就类似于使用 SQL 执行的数据库查询。也可以使用 Stream API 来并行执行操作。简而言之,Stream API 提供了一种高效且易于使用的处理数据的方式。
chenchenchen
2020/05/27
1.1K0
工作后, 你一定不能错过技术之JDK1.8的新特性
而在企业开发中, 主要用到的便是Lambda表达式和Stream流 , 而在下面,我们便主要的去学习这两方面的知识
时间静止不是简史
2020/07/27
6510
Java8 Stream,常用方法大合集
一、概述 二、分类 三、具体用法 1.流的常用创建方法 2.流的中间操作 3.流的终止操作
鱼找水需要时间
2023/02/16
4810
Java8 Stream,常用方法大合集
玩转Java8 Stream流
点击上方“芋道源码”,选择“设为星标” 管她前浪,还是后浪? 能浪的浪,才是好浪! 每天 10:33 更新文章,每天掉亿点点头发... 源码精品专栏 原创 | Java 2021 超神之路,很肝~ 中文详细注释的开源项目 RPC 框架 Dubbo 源码解析 网络应用框架 Netty 源码解析 消息中间件 RocketMQ 源码解析 数据库中间件 Sharding-JDBC 和 MyCAT 源码解析 作业调度中间件 Elastic-Job 源码解析 分布式事务中间件 TCC-Transaction
芋道源码
2022/08/31
5660
玩转Java8 Stream流
Java中lambda表达式详解
上面的代码中,e是一个lambda的对象,根据java的继承的特性,我们可以说e对象的类型是继承自eat接口。而e1是一个正常的匿名类的对象.
付威
2018/12/05
4.8K0
Java中lambda表达式详解
【Java8新特性】03 Stream流式数据处理
【Java8新特性】02 函数式接口和Lambda表达式实战练习:环绕执行模式使行为参数化
爱笑的架构师
2020/09/24
1.4K0
【Java8新特性】03 Stream流式数据处理
Java8 Stream
Java Stream函数式编程接口最初是在Java 8中引入的,并且与lambda一起成为Java开发的里程碑式的功能特性,它极大的方便了开放人员处理集合类数据的效率。
HLee
2021/08/12
1.8K0
Java8 Stream
java工程师要会的大数据之Stream流
在JDK8之前,Java是不⽀持函数式编程的,所谓的函数编程,即可理解是将⼀个函数(也称为“⾏ 为”)作为⼀个参数进⾏传递, ⾯向对象编程是对数据的抽象(各种各样的POJO类),⽽函数式编 程则是对⾏为的抽象(将⾏为作为⼀个参数进⾏传递
Joseph_青椒
2023/08/09
8050
【Java基础-1】 Java8新特性Stream详解
Java8的API中添加了一个新的特性: 流,即stream。stream是将数组或者集合的元素视为流,流在管道中流动过程中,对数据进行筛选、排序和其他操作。
云深i不知处
2020/09/16
1.2K0
JAVA——JDK 8新特性学习与整理
在自己工作、开发、学习中,特别是在阅读别人的项目源码时,总是能看到使用Lambda表达式和Stream流的代码写法,这两个新特性是JDK 8主要的特性和自己的编程风格。 Oracle 公司于 2014 年 3 月 18 日发布 Java 8,现如今已经JDK14都出来了,所以了解,阅读,和使用JDK 8新特性是非常有必要的,在面试的过程中也会经常被问到。
不愿意做鱼的小鲸鱼
2022/09/26
6150
JAVA——JDK 8新特性学习与整理
java8
Day1: ---- 1. Lambda表达式 Lambda表达式的基本结构: (param1,param2,param3,paramN) ->{body} 关于函数式接口: 如果一个接口只有一个抽象方法,那么该接口就是一个函数式接口。 如果我们在某个接口上声明了FunctionalInterface注解,那么编译器就会按照函数式接口的定义来要求该接口。 如果某个接口只有一个抽象方法,但我们没有给该接口声明FunctionalInterface注解,那么编译器依旧会将该接口看作是函数式接口。 ps(为什
如来
2020/07/14
9450
04-Java8新特性 Stream API
Java8中有两大最为重要的改变,第一个是Lambda表达式,另一个则是Stream API(java.util.stream.*)
彼岸舞
2021/12/14
9880
04-Java8新特性 Stream API
Stream 流解读
java.util.Stream 可以对元素列表进行一次或多次操作。Stream操作可以是中间值也可以是最终结果。最后的操作返回的是某种类型结果,而中间操作返回的是stream本身。因此你可以在一行代码链接多个方法调用。Streams被创建于java.util.Collection ,比如 list or set (map 并不支持)。Stream可以顺序执行,也可以并行执行。
微观技术
2020/08/20
7660
感受 Java Lambda 之美,推荐收藏,需要时查阅
来源:juejin.im/post/5ce66801e51d455d850d3a4a
芋道源码
2019/11/18
7140
强大的 Stream 函数式编程
Java8(又称为 Jdk1.8)是 Java 语言开发的一个主要版本。Oracle 公司于 2014 年 3 月 18 日发布 Java8,它支持函数式编程,新的 JavaScript 引擎,新的日期 API,新的 Stream API 等。Java8 API 添加了一个新的抽象称为流 Stream,可以让你以一种声明的方式处理数据。Stream API 可以极大提高 Java 程序员的生产力,让程序员写出高效率、干净、简洁的代码。
BUG弄潮儿
2021/07/22
2.9K0
java8 stream api流式编程
💛完整源码,请帮我点个star哦! java8自带常用的函数式接口 Predicate<T> boolean test(T t) 传入一个参数返回boolean值 Consumer<T> void accept(T t) 传入一个参数,无返回值 Function<T,R> R apply(T t) 传入一个参数,返回另一个类型 准备数据 //计算机俱乐部 private static List<Student> computerClub = Arrays.asList(
海向
2021/01/18
7920
java8 stream api流式编程
一篇文章让你搞懂Java8新特性
Java8 现在已经是标配了,但是相信很多小伙伴并没有系统的去了解总结相关知识点。接下来我为大家带来基本的总结。文章后面有总结笔记可以领取哦!期待您的关注。
@派大星
2023/06/28
1870
一篇文章让你搞懂Java8新特性
【Java8新特性】Stream API有哪些中间操作?看完你也可以吊打面试官!!
作者个人研发的在高并发场景下,提供的简单、稳定、可扩展的延迟消息队列框架,具有精准的定时任务和延迟队列处理功能。自开源半年多以来,已成功为十几家中小型企业提供了精准定时调度方案,经受住了生产环境的考验。为使更多童鞋受益,现给出开源框架地址:
冰河
2020/10/29
5880
【Java8新特性】Stream API有哪些中间操作?看完你也可以吊打面试官!!
java 1.8 stream使用总结(个人总结有一些经典文章的集合)
☀️相关笔记章节: 🌹java 1.8 stream使用总结(个人总结有一些经典文章的集合) 🌹遍历 ArrayList和遍历 Map的几种方式 🌹Java对象,Map,List,Set数组等相互转换大全(详细讲解,附代码,讲解案例) 🌹List,Map多层循环嵌套Demo及其理解 🌹List,Map 三种遍历方式:(总结理解) 🌹List<Map<String, Object>>,Map<String,List<Map<String, Object>>>多方式循环遍历
默 语
2024/11/20
2790
java 1.8 stream使用总结(个人总结有一些经典文章的集合)
Java8 通关攻略
Java8 特别强大的是Lambda 表达式和Stream,通过它两新增和增强了很多包
乔戈里
2020/02/14
1.3K0
Java8 通关攻略
相关推荐
Java 8 stream的使用示例
更多 >
交个朋友
加入腾讯云官网粉丝站
蹲全网底价单品 享第一手活动信息
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档