当前位置: 首页 > news >正文

Java之Stream其二

文章目录

  • 5,Optional
    • 5.1,概述
    • 5.2,使用
      • 5.2.1,创建对象
      • 5.2.2,安全消费值
      • 5.2.3,获取值
      • 5.2.4,安全获取值
      • 5.2.5,过滤
      • 5.2.6,判断
      • 5.2.7,数据转换
  • 6,方法引用
    • 6.1 推荐用法
    • 6.2 基本格式
    • 6.3 语法详解(了解)
      • 6.3.1 引用类的静态方法
      • 6.3.2 引用对象的实例方法
      • 6.3.3 引用类的实例方法
      • 6.3.4 构造器引用
  • 7,高级用法
    • 7.1,基本数据类型优化
    • 7.2,并行流

5,Optional

5.1,概述

​ 我们在编写代码的时候出现最多的就是空指针异常。所以在很多情况下我们需要做各种非空的判断。

​ 例如:

        Author author = getAuthor();if(author!=null){System.out.println(author.getName());}

​ 尤其是对象中的属性还是一个对象的情况下。这种判断会更多。

​ 而过多的判断语句会让我们的代码显得臃肿不堪。

​ 所以在JDK8中引入了Optional,养成使用Optional的习惯后你可以写出更优雅的代码来避免空指针异常。

​ 并且在很多函数式编程相关的API中也都用到了Optional,如果不会使用Optional也会对函数式编程的学习造成影响。

5.2,使用

5.2.1,创建对象

​ Optional就好像是包装类,可以把我们的具体数据封装Optional对象内部。然后我们去使用Optional中封装好的方法操作封装进去的数据就可以非常优雅的避免空指针异常。

​ 我们一般使用Optional静态方法ofNullable来把数据封装成一个Optional对象。无论传入的参数是否为null都不会出现问题。

Author author = getAuthor();
Optional<Author> authorOptional = Optional.ofNullable(author);

​ 你可能会觉得还要加一行代码来封装数据比较麻烦。但是如果改造下getAuthor方法,让其的返回值就是封装好的Optional的话,我们在使用时就会方便很多。

​ 而且在实际开发中我们的数据很多是从数据库获取的。Mybatis从3.5版本可以也已经支持Optional了。我们可以直接把dao方法的返回值类型定义成Optional类型,MyBastis会自己把数据封装成Optional对象返回。封装的过程也不需要我们自己操作。

​ 如果你确定一个对象不是空的则可以使用Optional静态方法of来把数据封装成Optional对象。

Author author = new Author();
Optional<Author> authorOptional = Optional.of(author);

​ 但是一定要注意,如果使用of的时候传入的参数必须不为null。(尝试下传入null会出现什么结果)

​ 如果一个方法的返回值类型是Optional类型。而如果我们经判断发现某次计算得到的返回值为null,这个时候就需要把null封装成Optional对象返回。这时则可以使用Optional静态方法empty来进行封装。

    Optional.empty()

​ 所以最后你觉得哪种方式会更方便呢?ofNullable

5.2.2,安全消费值

​ 我们获取到一个Optional对象后肯定需要对其中的数据进行使用。这时候我们可以使用其ifPresent方法对来消费其中的值。

​ 这个方法会判断其内封装的数据是否为空,不为空时才会执行具体的消费代码。这样使用起来就更加安全了。

​ 例如,以下写法就优雅的避免了空指针异常。

Optional<Author> authorOptional = Optional.ofNullable(getAuthor());authorOptional.ifPresent(author -> System.out.println(author.getName()));

5.2.3,获取值

​ 如果我们想获取值自己进行处理可以使用get()方法获取,但是不推荐。因为当Optional内部的数据为空的时候会出现异常。

5.2.4,安全获取值

​ 如果我们期望安全的获取值。我们不推荐使用get方法,而是使用Optional提供的以下方法。

  • orElseGet

    获取数据并且设置数据为空时的默认值。如果数据不为空就能获取到该数据。如果为空则根据你传入的参数来创建对象作为默认值返回。

    Optional<Author> authorOptional = Optional.ofNullable(getAuthor());
    Author author1 = authorOptional.orElseGet(() -> new Author());
    
  • orElseThrow

    获取数据,如果数据不为空就能获取到该数据。如果为空则根据你传入的参数来创建异常抛出。

    Optional<Author> authorOptional = Optional.ofNullable(getAuthor());
    try {Author author = authorOptional.orElseThrow((Supplier<Throwable>) () -> new RuntimeException("author为空"));System.out.println(author.getName());
    } catch (Throwable throwable) {throwable.printStackTrace();
    }
    

5.2.5,过滤

​ 我们可以使用filter方法对数据进行过滤。如果原本是有数据的,但是不符合判断,也会变成一个无数据的Optional对象。

        Optional<Author> authorOptional = Optional.ofNullable(getAuthor());authorOptional.filter(author -> author.getAge()>100).ifPresent(author -> System.out.println(author.getName()));

5.2.6,判断

​ 我们可以使用isPresent方法进行是否存在数据的判断。如果为空返回值为false,如果不为空,返回值为true。但是这种方式并不能体现Optional的好处,更推荐使用ifPresent方法

Optional<Author> authorOptional = Optional.ofNullable(getAuthor());if (authorOptional.isPresent()) {System.out.println(authorOptional.get().getName());
}

5.2.7,数据转换

​ Optional还提供了map可以让我们的对数据进行转换,并且转换得到的数据也还是被Optional包装好的,保证了我们的使用安全。

例如我们想获取作家的书籍集合。

    private static void testMap() {Optional<Author> authorOptional = getAuthorOptional();Optional<List<Book>> optionalBooks = authorOptional.map(author -> author.getBooks());optionalBooks.ifPresent(books -> System.out.println(books));}

6,方法引用

6.1 推荐用法

​ 我们在使用lambda时不需要考虑什么时候用方法引用,用哪种方法引用,方法引用的格式是什么。我们只需要在写完lambda方法发现方法体只有一行代码,并且是方法的调用时使用快捷键尝试是否能够转换成方法引用即可。

6.2 基本格式

类名或者对象名::方法名

6.3 语法详解(了解)

6.3.1 引用类的静态方法

其实就是引用类的静态方法

格式

类名::方法名

使用前提

​ 如果我们在重写方法的时候,方法体中只有一行代码,并且这行代码是调用了某个类的静态方法,并且我们把要重写的抽象方法中所有的参数都按照顺序传入了这个静态方法中,这个时候我们就可以引用类的静态方法。

例如:

如下代码就可以用方法引用进行简化

List<Author> authors = getAuthors();Stream<Author> authorStream = authors.stream();authorStream.map(author -> author.getAge()).map(age->String.valueOf(age));

注意,如果我们所重写的方法是没有参数的,调用的方法也是没有参数的也相当于符合以上规则。

优化后如下:

List<Author> authors = getAuthors();Stream<Author> authorStream = authors.stream();authorStream.map(author -> author.getAge()).map(String::valueOf);

6.3.2 引用对象的实例方法

格式

对象名::方法名

使用前提

​ 如果我们在重写方法的时候,方法体中只有一行代码,并且这行代码是调用了某个对象的成员方法,并且我们把要重写的抽象方法中所有的参数都按照顺序传入了这个成员方法中,这个时候我们就可以引用对象的实例方法

例如:

传进去一个参数name,并且按顺序传入了append的sb的成员方法中。

List<Author> authors = getAuthors();Stream<Author> authorStream = authors.stream();
StringBuilder sb = new StringBuilder();
authorStream.map(author -> author.getName()).forEach(name->sb.append(name));

优化后:

List<Author> authors = getAuthors();Stream<Author> authorStream = authors.stream();
StringBuilder sb = new StringBuilder();
authorStream.map(author -> author.getName()).forEach(sb::append);

6.3.3 引用类的实例方法

格式

类名::方法名

使用前提

​ 如果我们在重写方法的时候,方法体中只有一行代码,并且这行代码是调用了第一个参数的成员方法,并且我们把要重写的抽象方法中剩余的所有的参数都按照顺序传入了这个成员方法中,这个时候我们就可以引用类的实例方法。

例如:

调用了第一个参数str,且重写的抽象方法use剩余参数start和length按照顺序依次传入成员方法substring()中。

interface UseString{String use(String str,int start,int length);
}public static String subAuthorName(String str, UseString useString){int start = 0;int length = 1;return useString.use(str,start,length);
}
public static void main(String[] args) {subAuthorName("三更草堂", new UseString() {@Overridepublic String use(String str, int start, int length) {return str.substring(start,length);}});}

优化后如下:

public static void main(String[] args) {subAuthorName("三更草堂", String::substring);}

6.3.4 构造器引用

如果方法体中的一行代码是构造器的话就可以使用构造器引用。

格式

类名::new

使用前提

​ 如果我们在重写方法的时候,方法体中只有一行代码,并且这行代码是调用了某个类的构造方法,并且我们把要重写的抽象方法中的所有的参数都按照顺序传入了这个构造方法中,这个时候我们就可以引用构造器。

例如:

List<Author> authors = getAuthors();
authors.stream().map(author -> author.getName()).map(name->new StringBuilder(name)).map(sb->sb.append("-三更").toString()).forEach(str-> System.out.println(str));

优化后:

List<Author> authors = getAuthors();
authors.stream().map(author -> author.getName()).map(StringBuilder::new).map(sb->sb.append("-三更").toString()).forEach(str-> System.out.println(str));

7,高级用法

7.1,基本数据类型优化

​ 我们之前用到的很多Stream的方法由于都使用了泛型。所以涉及到的参数和返回值都是引用数据类型。

​ 即使我们操作的是整数小数,但是实际用的都是他们的包装类。JDK5中引入的自动装箱和自动拆箱让我们在使用对应的包装类时就好像使用基本数据类型一样方便。但是你一定要知道装箱和拆箱肯定是要消耗时间的。虽然这个时间消耗很下。但是在大量的数据不断的重复装箱拆箱的时候,你就不能无视这个时间损耗了。

​ 所以为了让我们能够对这部分的时间消耗进行优化。Stream还提供了很多专门针对基本数据类型的方法。

​ 例如:mapToInt,mapToLong,mapToDouble,flatMapToInt,flatMapToDouble等。

List<Author> authors = getAuthors();
authors.stream().map(author -> author.getAge()).map(age -> age + 10).filter(age->age>18).map(age->age+2).forEach(System.out::println);authors.stream().mapToInt(author -> author.getAge()).map(age -> age + 10).filter(age->age>18).map(age->age+2).forEach(System.out::println);

优化后:

使用.mapToInt(author -> author.getAge())后,流当中的元素都是int类型了,节省了时间。

authors.stream().mapToInt(author -> author.getAge()).map(age -> age + 10).filter(age->age>18).map(age->age+2).forEach(System.out::println);

7.2,并行流

当流中有大量元素时,我们可以使用并行流去提高操作的效率。其实并行流就是把任务分配给多个线程去完全。如果我们自己去用代码实现的话其实会非常的复杂,并且要求你对并发编程有足够的理解和认识。而如果我们使用Stream的话,我们只需要修改一个方法的调用就可以使用并行流来帮我们实现,从而提高效率。

  • parallel() 将顺序流转换为并行流,使得后续的操作(如 peekfilterreduce)可能在多个线程中并行执行。
private static void test() {Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);Integer sum = stream.parallel().peek(new Consumer<Integer>() {@Overridepublic void accept(Integer num) {System.out.println(num+Thread.currentThread().getName());}}).filter(num -> num > 5).reduce((result, ele) -> result + ele).get();System.out.println(sum);
}

执行结果:

7main
2ForkJoinPool.commonPool-worker-11
8ForkJoinPool.commonPool-worker-4
9ForkJoinPool.commonPool-worker-18
6main
1ForkJoinPool.commonPool-worker-11
5ForkJoinPool.commonPool-worker-29
4ForkJoinPool.commonPool-worker-4
10ForkJoinPool.commonPool-worker-18
3ForkJoinPool.commonPool-worker-25
40

为什么会打印多次num+mian?

主线程也被分配到任务,main 线程参与了并行流的处理,主线程处理了元素 7 和 6,说明这两个元素被分配给了主线程执行。

并行流执行流程示例:

  1. 主线程将 10 个元素分成 4 个子任务(例如每组 2~3 个元素)。
  2. 工作线程 worker-1worker-4 各领取一个子任务。
  3. 主线程发现还有剩余元素(如最后 2 个),直接处理它们。
  4. 所有线程并行执行 peekfilterreduce 操作。

也可以通过parallelStream直接获取并行流对象。

parallelStream() 是集合(如 ListSet 等)提供的一个方法,用于创建并行流。它与 stream().parallel() 的效果相同,但写法更简洁。并行流的核心特点是将流的处理任务分解到多个线程上并行执行,从而提高处理大量数据时的性能。

List<Author> authors = getAuthors();
authors.parallelStream().map(author -> author.getAge()).map(age -> age + 10).filter(age->age>18).map(age->age+2).forEach(System.out::println);

文章转载自:
http://astronomically.lbooon.cn
http://boughpot.lbooon.cn
http://amongst.lbooon.cn
http://causative.lbooon.cn
http://acetabula.lbooon.cn
http://annotinous.lbooon.cn
http://bigoted.lbooon.cn
http://anima.lbooon.cn
http://bonehead.lbooon.cn
http://admeasure.lbooon.cn
http://brooklet.lbooon.cn
http://automat.lbooon.cn
http://air.lbooon.cn
http://apartment.lbooon.cn
http://astropologist.lbooon.cn
http://awoken.lbooon.cn
http://astaticism.lbooon.cn
http://chilidog.lbooon.cn
http://albumose.lbooon.cn
http://aeacus.lbooon.cn
http://adherent.lbooon.cn
http://chorioallantois.lbooon.cn
http://adry.lbooon.cn
http://akyab.lbooon.cn
http://bolection.lbooon.cn
http://alcoholic.lbooon.cn
http://abattis.lbooon.cn
http://aeronaval.lbooon.cn
http://chromatype.lbooon.cn
http://binate.lbooon.cn
http://www.dtcms.com/a/281740.html

相关文章:

  • 第二章 OB 存储引擎高级技术
  • 数学金融与金融工程:学科差异与选择指南
  • 【AI News | 20250714】每日AI进展
  • 为 Git branch 命令添加描述功能
  • 将 Vue 3 + Vite + TS 项目打包为 .exe 文件
  • 711SJBH构建制造业信息化人才培训体系的对策-开题报告
  • 21-C#的委托简单使用-1
  • Datawhale 25年7月组队学习coze-ai-assistant Task1学习笔记:动手实践第一个AI Agent—英伦生活口语陪练精灵
  • yolov5、yolov8、yolov11、yolov12如何训练及轻量化部署-netron-onnx
  • echarts折线图的 线条的样式怎么控制
  • Python os模块完全指南:从入门到实战
  • python编程实现GUI界面的排序与查找算法动态模拟演示程序
  • Sa-Token使用要点
  • mongoDB安装初始化及简单介绍
  • 2025/7/15——java学习总结
  • Pandas 和 NumPy 使用文档整理
  • 大宗现货电子盘交易系统核心功能代码解析
  • QT6 源,六章事件系统(8)QEvent 的孙子类:QEnterEvent 光标进入
  • 无穿戴动捕如何凭借摄像头视觉识别算法,赋能高校专业教学革新?
  • python进阶
  • 145-变分模态分解VMD与平稳小波变换SWT信号降噪算法实现!
  • 4G模块 A7680通过MQTT协议连接到onenet(新版)
  • WebAPIs基本认知,DOM基础介绍
  • 基于Springboot+UniApp+Ai实现模拟面试小工具五:权限校验参数校验及日志功能实现
  • Ubuntu 22.04 安装 mysql-server与 postgreSQL 服务端
  • el-tooltip 快速滚动的时候出现残影如何解决 vue3
  • 30.安卓逆向2-frida hook技术-hook So文件(获取app加载的所有模块、导入函数、导出函数、hook SO函数)
  • 区块链发展史全景长图
  • 强化学习Reinforcement Learning
  • 基于Python的图像文字识别系统