46、Java 中 BigDecimal 类与 Lambda 和流的使用

Java 中 BigDecimal 类与 Lambda 和流的使用

在 Java 编程中,处理小数和集合数据是常见的需求。 BigDecimal 类为处理小数提供了可靠的解决方案,而 Lambda 表达式和流则为处理集合数据提供了强大而简洁的方式。

1. BigDecimal 类的使用

BigDecimal 类为 Java 开发者提供了一种可预测的方式来处理小数。

1.1 小数位数与舍入

BigDecimal 中,scale 指的是小数点右侧的数字位数。例如:

aNum = 23.45
bNum = 23.4
aNum scale = 2
bNum scale = 1

这里 bNum 的值为 23.4 而非 23.5,是向下舍入的结果。

1.2 基本数学运算

在使用 BigDecimal 进行数学计算时,需要使用该类提供的方法,而不是数学运算符。基本方法如下:
- add :加法
- subtract :减法
- multiply :乘法
- divide :除法

以下是一个简单的示例代码:

import java.math.BigDecimal;

public class App {
    public static void main(String[] args) {

        BigDecimal aNum = new BigDecimal("10");
        BigDecimal bNum = new BigDecimal("6");
        BigDecimal result;

        result = aNum.add(bNum);
        System.out.println("Adding: " + result);

        result = aNum.subtract(bNum);
        System.out.println("Subtracting: " + result);

        result = aNum.multiply(bNum);
        System.out.println("Multiplying: " + result);
    }
}

运行该代码,输出结果为:

Adding: 16
Subtracting: 4
Multiplying: 60
1.3 除法运算与舍入模式

在进行除法运算时,小数位数可能会增加,甚至可能出现无限循环小数。例如,10 除以 6 的结果是 1.6666…,这是一个无限循环小数。如果不指定舍入模式, BigDecimal 会抛出 ArithmeticException 异常。

为避免此问题,需要设置舍入模式,示例代码如下:

import java.math.BigDecimal;
import java.math.RoundingMode;

public class App {
    public static void main(String[] args) {

       BigDecimal aNum = new BigDecimal("10");
       BigDecimal bNum = new BigDecimal("6");
       BigDecimal result;

       result = aNum.divide(bNum, RoundingMode.HALF_UP);
       System.out.println("Adding: " + result);

       result = aNum.divide(bNum, 2, RoundingMode.HALF_UP);
       System.out.println("Subtracting: " + result);

       result = aNum.divide(bNum, 2, RoundingMode.DOWN);
       System.out.println("Multiplying: " + result);
    }
}

运行该代码,输出结果为:

2
1.67
1.66
1.4 重要要点总结
  • Scale 指的是小数点右侧的数字位数。
  • BigDecimal 提供了多种不同的舍入模式。
  • 在进行除法运算时,设置舍入模式非常重要,因为该操作可能会导致小数点右侧出现无限循环的值。
  • BigDecimal 对象是不可变的。
1.5 练习
  • 练习 1:利息计算器
    编写一个利息计算器程序,根据输入的年利率、初始本金和投资年限,计算投资后的总金额。新的季度金额计算公式为: CurrentBalance * (1 + (QuarterlyInterestRate / 100))
  • 练习 2:汽车销售服务层
    创建一个服务层,用于模拟汽车销售程序。该服务层需要实现一些业务逻辑,如根据 VIN 获取汽车、获取所有汽车、根据颜色过滤汽车、根据预算过滤汽车等。
2. Lambda 表达式与流的使用

流与 Lambda 表达式结合,使开发者能够以强大的方式处理集合中的数据。

2.1 聚合操作

聚合操作使用 Lambda 表达式对集合中的对象执行操作。例如:
- 打印 Address 对象集合中所有人的姓名。
- 返回来自俄亥俄州阿克伦市的所有人的 Address 对象。
- 按邮政编码对来自俄亥俄州阿克伦市的所有人的 Address 对象进行分组。
- 计算库存中服务器的平均使用年限。

2.2 管道与流

管道是一系列聚合操作的序列,流是从数据源通过管道传输项目的序列(不是数据结构)。管道包括数据源、零个或多个中间操作和一个终端操作。
- 数据源:最常见的是集合,也可以是数组、方法调用的返回值或某种 I/O 通道。
- 中间操作:如过滤操作,接受一个流并生成一个新的流。
- 终端操作:返回非流结果,如基本类型、集合或无结果。

以下是一个简单的 mermaid 流程图,展示了管道和流的关系:

graph LR
    A[数据源] --> B[中间操作1]
    B --> C[中间操作2]
    C --> D[终端操作]
    D --> E[结果]
2.3 流与迭代的比较

虽然一些聚合操作(如 forEach )看起来像迭代器,但它们有根本的区别:
- 聚合操作处理的是流中的项目,而不是直接处理集合中的项目。
- 聚合操作支持 Lambda 表达式作为参数。

2.4 Lambda 表达式

在编程中,Lambda 表达式是匿名函数或方法。在 Java 中,Lambda 表达式是函数式接口的实现,函数式接口是只有一个抽象方法的接口,如 Runnable Comparable 接口。

2.5 流方法的使用

以下是一些常见流方法的使用示例:

2.5.1 forEach() 方法

forEach() 方法是一个终端操作,用于对流中的每个对象执行代码。例如:

import java.util.ArrayList;
import java.util.List;

public class App {
    public static void main(String[] args) {

        List<Person> people = new ArrayList<>();

        people.add( new Person("Alfred", 17));
        people.add( new Person("Henrey", 18));
        people.add( new Person("George", 19));
        people.add( new Person("Joe", 27));
        people.add( new Person("Zelda", 7));

        for (Person currentPerson : people) {
            System.out.print(currentPerson.getName());
            System.out.print(" - ");
            System.out.println(currentPerson.getAge());
        }

        System.out.println("======");

        people.stream()
          .forEach((currentPerson) -> {
              System.out.print(currentPerson.getName());
              System.out.print(" - ");
              System.out.println(currentPerson.getAge());
        });       
    }
}

该代码使用 forEach() 方法打印 Person 对象列表中的每个人的姓名和年龄。

2.5.2 filter() 方法

filter() 方法用于过滤流,只包含满足条件的对象。例如,过滤出年龄大于等于 18 岁的人:

people.stream()
    .filter((p) -> p.getAge() >= 18)

这里的 Lambda 表达式 (p) -> p.getAge() >= 18 是一个谓词,返回布尔值。

通过使用 BigDecimal 类、Lambda 表达式和流,我们可以更高效、更简洁地处理小数和集合数据,提高代码的可读性和可维护性。

2.6 结合过滤与遍历操作

我们可以将 filter() 方法和 forEach() 方法结合使用,对集合中的数据进行过滤后再进行遍历操作。以下是一个示例代码:

package com.tsg.lambdafun;

import java.util.ArrayList;
import java.util.List;

public class App {

    public static void main(String[] args) {

        List<Person> people = new ArrayList<>();

        people.add( new Person("Alfred", 17));
        people.add( new Person("Henrey", 18));
        people.add( new Person("George", 19));
        people.add( new Person("Joe", 27));
        people.add( new Person("Zelda", 7));

        System.out.println("==> Age 18 or greater ==");
        people.stream()
            .filter((currentPerson) -> 
                    currentPerson.getAge() >= 18)
            .forEach((currentPerson) -> {
               System.out.print(currentPerson.getName());
               System.out.print(" - ");
               System.out.println(currentPerson.getAge());
        });       

        System.out.println("==> Names start with G ==");
        people.stream()
            .filter((currentPerson) -> 
                    currentPerson.getName().startsWith("G"))
            .forEach((currentPerson) -> {
               System.out.print(currentPerson.getName());
               System.out.print(" - ");
               System.out.println(currentPerson.getAge());
        });               
    }
}

在这个代码中,我们首先创建了一个包含多个 Person 对象的列表 people 。然后,我们使用 stream() 方法将列表转换为流,接着使用 filter() 方法进行过滤操作。第一次过滤出年龄大于等于 18 岁的人,第二次过滤出姓名以 G 开头的人。最后,使用 forEach() 方法遍历过滤后的流,并打印出符合条件的人的姓名和年龄。

运行该代码,输出结果如下:

==> Age 18 or greater ==
Henrey - 18
George - 19
Joe - 27
==> Names start with G ==
George - 19
2.7 不同类型的谓词

在 Java 中,Lambda 表达式返回布尔值的也可以称为谓词。除了普通的 Predicate 外,还有其他类型的谓词:
| 谓词类型 | 参数数量 | 参数类型 | 描述 |
| ---- | ---- | ---- | ---- |
| Predicate | 1 | 任意类型 | 接受一个参数并返回布尔值 |
| BiPredicate | 2 | 任意类型 | 接受两个参数并返回布尔值 |
| IntPredicate | 1 | int | 接受一个 int 类型的参数并返回布尔值 |
| LongPredicate | 1 | long | 接受一个 long 类型的参数并返回布尔值 |

3. 总结与应用场景

通过前面的介绍,我们了解了 BigDecimal 类、Lambda 表达式和流的使用。下面我们来总结一下它们的应用场景:

3.1 BigDecimal 类的应用场景
  • 金融计算 :在金融领域,对小数的精度要求非常高, BigDecimal 类可以避免浮点数计算带来的精度丢失问题。例如,在银行系统中进行利息计算、货币兑换等操作时,使用 BigDecimal 类可以确保计算结果的准确性。
  • 商业计算 :在商业应用中,如商品价格计算、折扣计算等,也需要精确的小数计算, BigDecimal 类可以满足这些需求。
3.2 Lambda 表达式与流的应用场景
  • 数据处理 :当需要对集合中的数据进行过滤、映射、排序等操作时,使用 Lambda 表达式和流可以使代码更加简洁、易读。例如,在一个员工信息管理系统中,需要筛选出年龄大于 30 岁的员工,或者计算所有员工的平均工资等操作,使用流和 Lambda 表达式可以轻松实现。
  • 并行处理 :流支持并行处理,可以充分利用多核处理器的性能,提高数据处理的效率。例如,在处理大量数据时,可以使用并行流来加速计算过程。

以下是一个 mermaid 流程图,展示了在实际应用中如何选择使用 BigDecimal 类、Lambda 表达式和流:

graph LR
    A[需求场景] --> B{是否需要精确小数计算}
    B -- 是 --> C[使用 BigDecimal 类]
    B -- 否 --> D{是否需要处理集合数据}
    D -- 是 --> E[使用 Lambda 表达式和流]
    D -- 否 --> F[使用常规方法]

通过合理使用 BigDecimal 类、Lambda 表达式和流,我们可以提高代码的质量和性能,使程序更加健壮和高效。在实际开发中,我们应该根据具体的需求场景选择合适的技术和方法,以达到最佳的开发效果。

内容概要:本文介绍了一个基于MATLAB实现的无人机三维路径规划项目,采用蚁群算法(ACO)多层感知机(MLP)相结合的混合模型(ACO-MLP)。该模型通过三维环境离散化建模,利用ACO进行全局路径搜索,并引入MLP对环境特征进行自适应学习启发因子优化,实现路径的动态调整多目标优化。项目解决了高维空间建模、动态障碍规避、局部最优陷阱、算法实时性及多目标权衡等关键技术难题,结合并行计算参数自适应机制,提升了路径规划的智能性、安全性工程适用性。文中提供了详细的模型架构、核心算法程及MATLAB代码示例,涵盖空间建模、信息素更新、MLP训练融合优化等关键步骤。; 适合人群:具备一定MATLAB编程基础,熟悉智能优化算法神经网络的高校学生、科研人员及从事无人机路径规划相关工作的工程师;适合从事智能无人系统、自动驾驶、机器人导航等领域的研究人员; 使用场景及目标:①应用于复杂三维环境下的无人机路径规划,如城市物、灾害救援、军事侦察等场景;②实现飞行安全、能耗优化、路径平滑实时避障等多目标协同优化;③为智能无人系统的自主决策环境适应能力提供算法支持; 阅读建议:此资源结合理论模型MATLAB实践,建议读者在理解ACOMLP基本原理的基础上,结合代码示例进行仿真调试,重点关注ACO-MLP融合机制、多目标优化函数设计及参数自适应策略的实现,以深入掌握混合智能算法在工程中的应用方法。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值