来聊聊java8的数值流

news2024/11/29 8:35:00

简介

java8为我提供的简单快捷的数值流计算API,本文就基于几个常见的场景介绍一下数值流API的使用。

基础示例

我们以一个食物热量计算的功能展开演示,如下所示,可以看到Dish类它记录了每一个食物的名称、热量、类型等信息:

public class Dish {

    /**
     * 名称
     */
    private final String name;
    /**
     * 是否是素食
     */
    private final boolean vegetarian;
    /**
     * 卡路里
     */
    private final int calories;
    /**
     * 类型
     */
    private final Type type;

    //类型枚举 分别是是:肉类 鱼类 其他
    public enum Type {MEAT, FISH, OTHER}

    public Dish(String name, boolean vegetarian, int calories, Type type) {
        this.name = name;
        this.vegetarian = vegetarian;
        this.calories = calories;
        this.type = type;
    }

    //...... get set


}

基于这个食物类,我们给出一个食物类的集合作为模拟数据:

public static final List<Dish> menuList =
            Arrays.asList(
                    new Dish("pork", false, 800, Dish.Type.MEAT),
                    new Dish("beef", false, 700, Dish.Type.MEAT),
                    new Dish("chicken", false, 400, Dish.Type.MEAT),
                    new Dish("french fries", true, 530, Dish.Type.OTHER),
                    new Dish("rice", true, 350, Dish.Type.OTHER),
                    new Dish("season fruit", true, 120, Dish.Type.OTHER),
                    new Dish("pizza", true, 550, Dish.Type.OTHER),
                    new Dish("prawns", false, 400, Dish.Type.FISH),
                    new Dish("salmon", false, 450, Dish.Type.FISH)
            );

我们希望计算出这个菜肴集合的总热量,我们可能会这样写:

 public static void main(String[] args) {
        
        int total = menuList.stream()
                //获取每个食物的卡路里
                .map(Dish::getCalories)
                //调用reduce,从0开始累加每个食物的热量
                .reduce(0, Integer::sum);

        System.out.println(total);
    }

输出结果如下:

4300

尽管它尽可能的简洁并计算出了总热量,但是它存在许多隐患,首先时map时,它会将基本类型的calories装箱成Integer,这一点我们查看map的返回值即可知晓。

Stream<Integer> integerStream = menuList.stream()
                //获取每个食物的卡路里
                .map(Dish::getCalories);

因为拿到的是包装类的流,调用reduce进行数值计算时,有需要对其进行拆箱,拆箱时就会调用到IntegerintValue方法:

public int intValue() {
        return value;
    }

所以若在大量数值计算的情况下,频繁的拆箱和装箱势必导致程序的执行效率低下。

在这里插入图片描述

特值流

那么有没有什么办法可以保证在数据收集的时候避免频繁装箱和拆箱呢?答案是特化流,就以本案例来说,我们在数值收集的时候直接调用mapToInt方法,通过该方法即可得到每一个数值的特值流IntStream,随后我们直接调用特值流计算方法sum即可完成热量统计:

在这里插入图片描述

对应的代码示例如下:

public static void main(String[] args) {
        int total = menuList.stream()
                //将每一个卡路里转换为特值流IntStream
                .mapToInt(Dish::getCalories)
                //将所有数值累加
                .sum();

        System.out.println(total);
    }

最终输出结果也是4300:

4300

相较于reduce方法,特值流提供了更多更方便的计算API

  1. average:计算所有数值的平均数。
  2. count:获取数值总数。
  3. max:获取收集数据中的最大值。
  4. min:获取收集数据中的最小值。

特化流还原会原始流

有时候我们希望这些特化流转为原始流即包装类的流,那么我们可直接调用boxed方法完成对特值流的装箱:

 public static void main(String[] args) {

        
        Stream<Integer> integerStream = menuList.stream()
                //拿到所有数值的特值流
                .mapToInt(Dish::getCalories)
                //将所有特值流装箱
                .boxed();

        //输出特值流对象的数值
        integerStream.forEach(i -> System.out.println(i));

    }

特化流空数值问题

我们都知道特化流可以直接获取收集到数值的最大值或者最小值,我们假设这样一个场景,食物类对象的卡路里字段为Integer

private final Integer calories;

并且我们食物类的集合为空:

 public static final List<Dish> menuList = new ArrayList<>();

面对可能存在的空结果问题,要如何解决呢?
实际上java8已经考虑到这个问题了,当我们调用max等计算API获取结果时,它实际返回的对象是OptionalInt,该对象提供了各种API用于判断数值是否为空,当我们最大值为空,就直接返回1时,我们可以直接使用orElse方法:

public static void main(String[] args) {

        OptionalInt max = menuList.stream()
                .mapToInt(Dish::getCalories)
                .max();
        
        //不存在最大值时,直接返回1
        System.out.println(max.orElse(0));


    }

亦或者我们需要判断是否存在最大值时,可以直接调用isPresent方法:

public static void main(String[] args) {

        OptionalInt max = menuList.stream()
                .mapToInt(Dish::getCalories)
                .max();

        //若存在最大值直接返回true
        System.out.println(max.isPresent());


    }

数值流的范围操作

我们希望统计1-100之间的偶数数量,在java8之前,你可能会这样做:

  1. for循环1-100。
  2. 判断是否是偶数。
  3. 如果是偶数,则临时变量count自增一下。

java8的步骤则精简许多:

  1. 基于特值流生成1-100全闭区间数据。
  2. 过滤出偶数。
  3. 调用count进行统计。
public static void main(String[] args) {
        //生成1-100全闭区间数据
        long count = IntStream.rangeClosed(1, 100)
                //过滤出偶数
                .filter(i -> i % 2 == 0)
                //计算统计结果
                .count();

        System.out.println(count);


    }

输出结果:

50

当然,如果你要生成左闭右开即1-99,则可以调用range方法生成:

IntStream.range(1, 100)

数值流的应用——勾股数

现在我们来写一个获取1-100以内前3个勾股数的小功能。由公式:

a^2 + b^2=c^2

可知,要想得到勾股数,我们只需判断a^2 + b^2的和再开根号是否可以被整除,即:

Math.sqrt(a * a + b * b) % 1 == 0

所以我们可以按照下面这样的步骤执行:

  1. 创建1-100全闭区间作为第一条边a。
  2. 为避免计算的勾股数重复,出现[3,4,5][4,3,5]这种情况,我们的第二条边b范围为a-100。
  3. 拿着a和b,计算这两个数值的平方和再开根号看看是否为整数。
  4. 将开根号结果为整数的结果生成数组。
  5. 获取前3个这样的数组。

在这里插入图片描述

所以我们写出下面这段代码,需要注意的是笔者在生成b的时候用到了flatMap,原因很简单,因为生成a时boxed返回的对象是Stream<Integer>,假如把这个流直接用map和b进行映射操作的话,最终结果只能是[Stream<Integer>,Integer,Integer],所以我们需要使用flatMap将a进行扁平化从而得到一个Integer

public static void main(String[] args) {
        //生成a
        Stream<int[]> result = IntStream.rangeClosed(1, 100).boxed()
                //基于a的范围生成 a-100范围的b,并过滤出平方再开方后可以整除的b,构成数组
                .flatMap(a -> IntStream.rangeClosed(a, 100).filter(b -> Math.sqrt(a * a + b * b) % 1 == 0).boxed().map(b -> new int[]{a, b, (int) Math.sqrt(a * a + b * b)}))
                //取前3个
                .limit(3);

        //打印输出
        result.forEach(r -> System.out.println(r[0] + " " + r[1] + " " + r[2]));


    }

最终输出结果如下:

3 4 5
5 12 13
6 8 10

但是这种写法不够好,可以看到我们得到合适a和b时,还需要手动调用boxed将其还原为原始流,再用map映射为数组,这样实在太麻烦了。

还记得我们特化流还原为原始流的一个方法mapToxxx方法吗?如果我们希望将其转为数组,我们在得到a和b之后,直接调用mapToObj,代码一步到位:


public static void main(String[] args) {
    //生成a
    Stream<int[]> result = IntStream.rangeClosed(1, 100).boxed()
            //基于a的范围生成 a-100范围的b,并过滤出平方再开方后可以整除的b,构成数组
            .flatMap(a -> IntStream.rangeClosed(a, 100).filter(b -> Math.sqrt(a * a + b * b) % 1 == 0).mapToObj(b -> new int[]{a, b, (int) Math.sqrt(a * a + b * b)}))
            //取前3个
            .limit(3);

    //打印输出
    result.forEach(r -> System.out.println(r[0] + " " + r[1] + " " + r[2]));


}

参考资料

Java 8 in Action:https://book.douban.com/subject/25912747/

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1300004.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

百科词条可以删除吗?如何删除自己的百度百科?

近日&#xff0c;小马识途营销顾问接到不少客户删除自己百科词条的咨询&#xff0c;有不少人自己并没有去建立百科词条&#xff0c;但是网上已经有了&#xff0c;有的信息不正确&#xff0c;甚至有的信息是负能量的&#xff0c;对当事人自己造成一定的困扰&#xff0c;所以寻求…

【Proteus仿真】【51单片机】光照强度检测系统

文章目录 一、功能简介二、软件设计三、实验现象联系作者 一、功能简介 本项目使用Proteus8仿真51单片机控制器&#xff0c;使共阴数码管&#xff0c;PCF8591 ADC模块、光敏传感器等。 主要功能&#xff1a; 系统运行后&#xff0c;数码管显示光传感器采集光照强度值&#xff…

在线测试http接口,为您解析最佳测试方法

您是否正在寻找一种方便、高效且可靠的方法来测试您的http接口&#xff1f;在这篇文章中&#xff0c;我们将为您介绍在线测试http接口的最佳方法&#xff0c;帮助您确保您的接口在各种情况下都能正常运行。 什么是http接口&#xff1f; 在开始介绍如何测试http接口之前&#x…

json精讲

本文介绍json的规范及javascript和java对数据的交换读取 1. json介绍1.1 json简介1.2为什么使用 JSON&#xff1f; 2. json规范2.1基础规范2.2 key值为-字符串、数字、布尔值2.3 key值为对象Object2.4 key值为数组2.5 json本身就是一个数组 3.javascript操作json3.1 javascript…

git 本地有改动,远程也有改动,且文件是自动生成的配置文件

在改动过的地方 文件是.lock文件&#xff0c;自动生成的。想切到远程的分支&#xff0c;但是远程的分支也有改动过。这时候就要解决冲突&#xff0c;因为这是两个分支&#xff0c;代码都是不一样的&#xff0c;要先把这改动的代码提交在本地或者提交在本分支的远程才可以切到其…

freeswitch webrtc video_demo客户端进行MCU的视频会议

系统环境 一、编译服务器和加载模块 二、下载编译指定版本video_demo 三、配置verto.conf.xml 1.修改配置文件 2.重新启动 四、MCU通话测试 1.如何使用video_demo 2.测试结果 五、MCU的通话原理及音频/视频/布局/管理员等参数配置 附录 freeswitch微信交流群 系统环境 lsb_rel…

lv11 嵌入式开发 IIC(下) 20

目录 1 Exynos4412下IIC控制器介绍 1.1 总览 1.2 特征 1.3 工作框图 1.4 其他内容介绍 1.5 四种工作模式寄存器流程 2 IIC寄存器详解 2.1 概述 2.2 控制寄存器 2.3 状态寄存器 2.4 地址寄存器 2.5 数据寄存器 2.6 其他寄存器 3 MPU06050 3.1 简介 3.2 MPU6050主…

【已解决】解决UbuntuKali无法进行SSH远程连接

目录 Ubuntu20.04配置SSH远程连接Kali Linux配置SSH远程连接 Ubuntu20.04配置SSH远程连接 首先更新安装包 sudo apt-get update 下载SSH服务 sudo apt install openssh-server 查看SSH服务 service ssh status 打开 /etc/ssh/sshd_config文件修改配置文件 将PermitRootLog…

智能优化算法应用:基于郊狼算法3D无线传感器网络(WSN)覆盖优化 - 附代码

智能优化算法应用&#xff1a;基于郊狼算法3D无线传感器网络(WSN)覆盖优化 - 附代码 文章目录 智能优化算法应用&#xff1a;基于郊狼算法3D无线传感器网络(WSN)覆盖优化 - 附代码1.无线传感网络节点模型2.覆盖数学模型及分析3.郊狼算法4.实验参数设定5.算法结果6.参考文献7.MA…

思科最新版Cisco Packet Tracer 8.2.1安装

思科最新版Cisco Packet Tracer 8.2.1安装 一. 注册并登录CISCO账号二. 下载 Cisco Packet Tracer 8.2.1三. 安装四. 汉化五. cisco packet tracer教学文档六. 正常使用图 前言 这是我在这个网站整理的笔记,有错误的地方请指出&#xff0c;关注我&#xff0c;接下来还会持续更新…

【数据结构第 6 章 ③】- 用 C 语言实现邻接表并简单介绍十字链表和邻接多重表

目录 一、邻接表 1.1 - ALGraph.h 1.2 - ALGraph.c 1.3 - Test.c 二、十字链表 三、邻接多重表 一、邻接表 邻接表&#xff08;Adjacency List&#xff09;是图的一种链式存储结构。在邻接表中&#xff0c;对图中每个顶点建立一个单链表&#xff0c;第 i 个单链表中的结…

使用ssm框架进行mapper或者service单纯测试时报错问题处理

问题 使用注解方式配置好ssm后&#xff0c;无法在测试中单独测试mapper层和service层。效果如下&#xff1a; 解决 关闭配置类中的EnableWebMvc 再次运行&#xff0c;即可完成测试。

【WPF 按钮点击后异步上传多文件code示例】

前言: WPF中按钮点击事件如何执行时间太长会导致整个UI线程卡顿&#xff0c;现象就是页面刷新卡住&#xff0c;点击其他按钮无反馈。如下是进行异步执行命令&#xff0c;并远程上传文件的代码。 ![异步上传文件](https://img-blog.csdnimg.cn/direct/20c071929b004dcf9223dee2…

postgresql安装部署(docker版本)

1.在线部署 创建数据库存储目录 mkdir /home/pgdata创建容器 docker run --name postgresql --restartalways -d -p 5432:5432 -v /home/pgdata:/var/lib/postgresql/data --shm-size10g -e POSTGRES_PASSWORD密码 postgis/postgis:12-3.2-alpine–name为设置容器名称 -d表…

第 375 场 LeetCode 周赛题解

A 统计已测试设备 模拟&#xff1a;记录当前已测试设备数量 class Solution { public:int countTestedDevices(vector<int> &batteryPercentages) {int res 0;int s 0;for (auto x: batteryPercentages) {if (x - s > 0) {res;s;}}return res;} };B 双模幂运算 …

NR Channel raster和Sync raster

NR中&#xff0c;由于信道带宽可能非常大&#xff0c;如果UE按照channel Raster进行同步信号搜索&#xff0c;需要的时间很长&#xff0c;且非常耗电&#xff1b;因而NR引入了Synchronization raster的概念&#xff0c;同步信号按照Sync Raster放置。 ARFCN 频点号对应Channel…

java--DateTimeFormatter、Period、Duration

1.DateTimeFormatter 2.LocalDateTime提供的格式化、解析时间的方法 3.Period(一段时期) 可以用于计算两个LocalDate对象相差的年数、月数、天数。 4.Duration(持续时间) 可以用于计算两个时间对象相差的天数、小时数、分数、秒数、纳秒数&#xff1b;支持LocalTime、LocalDa…

【Spring】依赖注入之属性注入详解

前言&#xff1a; 我们在进行web开发时&#xff0c;基本上一个接口对应一个实现类&#xff0c;比如IOrderService接口对应一个OrderServiceImpl实现类&#xff0c;给OrderServiceImpl标注Service注解后&#xff0c;Spring在启动时就会将其注册成bean进行统一管理。在Co…

【小沐学Python】Python实现WebUI网页图表(gradio)

文章目录 1、简介2、安装3、基本测试3.1 入门代码3.2 组件属性3.3 多个输入和输出组件3.4 图像示例3.5 聊天机器人3.6 模块&#xff1a;更灵活、更可控3.7 进度条 结语 1、简介 https://www.gradio.app/ Gradio是用友好的网络界面演示机器学习模型的最快方法&#xff0c;因此…

C# WPF上位机开发(动态库dll的开发)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 很多时候&#xff0c;我们并不希望所有的程序都放到一个exe里面。因为这样相当于把所有的风险都放在了一个文件里里面&#xff0c;既不利于程序的升…