使用 Java Stream API,查找变量的最高值,以及对变量所做的更改的流

2023-12-26

背景/场景

假设我们有一个不可变的对象,名为Transaction, where transaction.getAction()会返回一个TransactionAction enum这可以是DEPOSIT or WITHDRAW, and transaction.getAmount()会返回一个Integer其中指定存款或取款的金额。

enum TransactionAction {
    WITHDRAW,
    DEPOSIT
}

public class Transaction {

    private final TransactionAction action;
    private final int amount;

    public Transaction(TransactionAction action, int amount) {
        this.action = action;
        this.amount = amount;
    }

    public TransactionAction getAction() {
        return action;
    }

    public int getAmount() {
        return amount;
    }

}

Question

我们现在有一个Stream<Transaction>这是一条充满了Transaction那可以是DEPOSIT or WITHDRAW。我们可以想象这个Stream<Transaction> as a 交易历史 of 一个特定的银行帐户.

我想要实现的是获得最高余额该帐户曾经取得过最有效率的方式(因此使用 Stream API)。


Example

鲍勃的交易历史是:

// balance start at 0
[DEPOSIT]   1200        // balance: 1200
[DEPOSIT]   500         // balance: 1700
[WITHDRAW]  700         // balance: 1000
[DEPOSIT]   300         // balance: 1300
[WITHDRAW]  800         // balance: 500
[WITHDRAW]  500         // balance: 0

Bob 的最高余额是1700.


您需要的是找到累积和的最大值。在伪代码中,这将类似于:

transactions = [1200, 500, -700, 300, -800, -500]
csum = cumulativeSum(transactions) // should be [1200,1700,1000,1300,500,0]
max(csum) // should be 1700

势在必行的方式:

传统的 for 循环非常适合这种情况。它应该相当容易编写,并且可能是时间和空间上最有效的替代方案。它不需要多次迭代,也不需要额外的列表。

int max = 0;
int csum = 0;
for (Transaction t: transactions) {
    int amount = (t.getAction() == TransactionAction.WITHDRAW ? -1 : 1) * t.getAmount();
    csum += amount;
    if (csum > max) max = csum;
}

深入探讨功能:

流是一种函数式编程概念,因此它们没有副作用,并且非常适合无状态操作。保持累积状态被认为是一种副作用,然后我们必须讨论 Monad 来控制这些副作用……我们不想走那条路。

Java 不是一种函数式语言(尽管允许函数式风格),因此不太关心纯度。您可以简单地在流外部有一个控制变量来跟踪当前的外部状态map or reduce运营。但这也将放弃 Stream 的一切用途。

那么我们来看看Java的有经验的小伙伴们在这件事情上是怎么做的呢?在纯 Haskell 中,累积和可以通过 Scan Left 操作来实现:

λ> scanl1 (+) [1200, 500, -700, 300, -800, -500] 
[1200,1700,1000,1300,500,0]

找到最大值就像这样简单:

λ> maximum ( scanl1 (+) [1200, 500, -700, 300, -800, -500] )
1700

Java Streams 解决方案:

Java 没有这种表达左扫描的惯用方式,但是您可以使用以下命令获得类似的结果collect.

transactions.stream()
    .map(t -> (t.getAction() == TransactionAction.WITHDRAW ? -1 : 1) * t.getAmount())
    .collect(ArrayList<Integer>::new, (csum, amount) -> 
        csum.add(csum.size() > 0 ? csum.get(csum.size() - 1) + amount : amount), 
        ArrayList::addAll)
    .stream()
    .max(Integer::compareTo);
// returns Optional[1700]

EDIT:正如评论中正确指出的那样,该累加器函数不具有关联性,如果尝试使用则会出现问题parallelStream代替stream.

这可以进一步简化。例如,如果您使用乘数丰富 TransactionAction 枚举(-1 表示WITHDRAW和 1 为DEPOSIT), then map可以替换为:

.map(t -> t.getAction().getMultiplier() * t.getAmount())

编辑:另一种方法:并行前缀和

从 Java 8 开始,数组提供了parallelPrefix可以使用如下操作:

Integer[] amounts = transactions.stream()
    .map(t -> (t.getAction() == TransactionAction.WITHDRAW ? -1 : 1) * t.getAmount())
    .toArray(Integer[]::new);

Arrays.parallelPrefix(amounts, Integer::sum);

Arrays.stream(amounts).max(Integer::compareTo); 
// returns Optional[1700]

作为流collect,它还需要一个关联函数,Integer::sum满足该性质。缺点是它需要一个数组并且不能与列表一起使用。虽然parallelPrefix非常高效,设置阵列来使用它并没有什么回报。

包起来:

同样,使用 Java Streams 可以实现这一点,尽管它在时间和空间上不如传统循环那么高效。但您可以从流的组合性中受益。一如既往,这是一种权衡。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

使用 Java Stream API,查找变量的最高值,以及对变量所做的更改的流 的相关文章

  • Java中反射是如何实现的?

    Java 7 语言规范很早就指出 本规范没有详细描述反射 我只是想知道 反射在Java中是如何实现的 我不是问它是如何使用的 我知道可能没有我正在寻找的具体答案 但任何信息将不胜感激 我在 Stackoverflow 上发现了这个 关于 C
  • Play框架运行应用程序问题

    每当我尝试运行使用以下命令创建的新 Web 应用程序时 我都会收到以下错误Play http www playframework org Error occurred during initialization of VM Could no
  • 制作一个交互式Windows服务

    我希望我的 Java 应用程序成为交互式 Windows 服务 用户登录时具有 GUI 的 Windows 服务 我搜索了这个 我发现这样做的方法是有两个程序 第一个是服务 第二个是 GUI 程序并使它们进行通信 服务将从 GUI 程序获取
  • 多个 Maven 配置文件激活多个 Spring 配置文件

    我想在 Maven 中构建一个环境 在其中我想根据哪些 Maven 配置文件处于活动状态来累积激活多个 spring 配置文件 目前我的 pom xml 的相关部分如下所示
  • 加速代码 - 3D 数组

    我正在尝试提高我编写的一些代码的速度 我想知道从 3d 整数数组访问数据的效率如何 我有一个数组 int cube new int 10 10 10 我用价值观填充其中 然后我访问这些值数千次 我想知道 由于理论上所有 3d 数组都存储在内
  • 反射找不到对象子类型

    我试图通过使用反射来获取包中的所有类 当我使用具体类的代码 本例中为 A 时 它可以工作并打印子类信息 B 扩展 A 因此它打印 B 信息 但是当我将它与对象类一起使用时 它不起作用 我该如何修复它 这段代码的工作原理 Reflection
  • 路径中 File.separator 和斜杠之间的区别

    使用有什么区别File separator和一个正常的 在 Java 路径字符串中 与双反斜杠相反 平台独立性似乎不是原因 因为两个版本都可以在 Windows 和 Unix 下运行 public class SlashTest Test
  • 斯坦福 NLP - 处理文件列表时 OpenIE 内存不足

    我正在尝试使用斯坦福 CoreNLP 中的 OpenIE 工具从多个文件中提取信息 当多个文件 而不是一个 传递到输入时 它会给出内存不足错误 All files have been queued awaiting termination
  • 如何在PreferenceActivity中添加工具栏

    我已经使用首选项创建了应用程序设置 但我注意到 我的 PreferenceActivity 中没有工具栏 如何将工具栏添加到我的 PreferenceActivity 中 My code 我的 pref xml
  • Java按日期升序对列表对象进行排序[重复]

    这个问题在这里已经有答案了 我想按一个参数对对象列表进行排序 其日期格式为 YYYY MM DD HH mm 按升序排列 我找不到正确的解决方案 在 python 中使用 lambda 很容易对其进行排序 但在 Java 中我遇到了问题 f
  • 如何将 pfx 文件转换为 jks,然后通过使用 wsdl 生成的类来使用它来签署传出的肥皂请求

    我正在寻找一个代码示例 该示例演示如何使用 PFX 证书通过 SSL 访问安全 Web 服务 我有证书及其密码 我首先使用下面提到的命令创建一个 KeyStore 实例 keytool importkeystore destkeystore
  • 如何在 javadoc 中使用“<”和“>”而不进行格式化?

    如果我写
  • 仅将 char[] 的一部分复制到 String 中

    我有一个数组 char ch 我的问题如下 如何将 ch 2 到 ch 7 的值合并到字符串中 我想在不循环 char 数组的情况下实现这一点 有什么建议么 感谢您花时间回答我的问题 Use new String value offset
  • Google App Engine 如何预编译 Java?

    App Engine 对应用程序的 Java 字节码使用 预编译 过程 以增强应用程序在 Java 运行时环境中的性能 预编译代码的功能与原始字节码相同 有没有详细的信息这是做什么的 我在一个中找到了这个谷歌群组消息 http groups
  • 声明的包“”与预期的包不匹配

    我可以编译并运行我的代码 但 VSCode 中始终显示错误 早些时候有一个弹出窗口 我不记得是什么了 我点击了 全局应用 从那以后一直是这样 Output is there but so is the error The declared
  • 获取 JVM 上所有引导类的列表?

    有一种方法叫做findBootstrapClass对于一个类加载器 如果它是引导的 则返回一个类 有没有办法找到类已经加载了 您可以尝试首先通过例如获取引导类加载器呼叫 ClassLoader bootstrapLoader ClassLo
  • 有没有办法为Java的字符集名称添加别名

    我收到一个异常 埋藏在第 3 方库中 消息如下 java io UnsupportedEncodingException BIG 5 我认为发生这种情况是因为 Java 没有定义这个名称java nio charset Charset Ch
  • 当我从 Netbeans 创建 Derby 数据库时,它存储在哪里?

    当我从 netbeans 创建 Derby 数据库时 它存储在哪里 如何将它与项目的其余部分合并到一个文件夹中 右键单击Databases gt JavaDB in the Service查看并选择Properties This will
  • JGit 检查分支是否已签出

    我正在使用 JGit 开发一个项目 我设法删除了一个分支 但我还想检查该分支是否已签出 我发现了一个变量CheckoutCommand但它是私有的 private boolean isCheckoutIndex return startCo
  • java.lang.IllegalStateException:驱动程序可执行文件的路径必须由 webdriver.chrome.driver 系统属性设置 - Similiar 不回答

    尝试学习 Selenium 我打开了类似的问题 但似乎没有任何帮助 我的代码 package seleniumPractice import org openqa selenium WebDriver import org openqa s

随机推荐