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

使用Java流API,查找变量的最高值,以及对变量所做更改的流 背景/情景,java,java-stream,Java,Java Stream,假设我们有一个名为Transaction的不可变对象,其中Transaction.getAction()将返回一个TransactionAction枚举,该枚举可以是DEPOSIT或draw,以及Transaction.getAmount()将返回一个指定存款或取款金额的整数 enum TransactionAction { WITHDRAW, DEPOSIT } public class Transaction { private final Transaction

假设我们有一个名为
Transaction
的不可变对象,其中
Transaction.getAction()
将返回一个
TransactionAction
枚举,该枚举可以是
DEPOSIT
draw
,以及
Transaction.getAmount()
将返回一个指定存款或取款金额的
整数

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;
    }

}

问题: 我们现在有一个
,它是一个充满
交易
的流,可以是
存款
取款
。我们可以将此
想象为一个特定银行账户的交易历史

我试图实现的是以最有效的方式(因此使用流API)获得帐户有史以来的最高余额


例子 Bob交易历史记录为:

// 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

鲍勃的最高余额是1700英镑

在这里,一条流没有帮助。使用列表和for循环:

List<Transaction> transactions = ...;

int balance = 0;
int max = 0;
for (Transaction transaction : transactions) {
    balance += (transaction.getAction() == TransactionAction.DEPOSIT ? 1 : -1) 
                * transaction.getAmount();
    max = Math.max(max, balance);
}
列出事务=。。。;
国际收支平衡=0;
int max=0;
用于(交易:交易){
余额+=(transaction.getAction()==TransactionAction.DEPOSIT?1:-1)
*transaction.getAmount();
最大值=数学最大值(最大值,平衡);
}

问题是,在处理事务时,您需要跟踪某些状态,如果不引入复杂或可变的数据结构,您将无法在流中实现这一点,而这些数据结构会使代码容易出现错误。

下面是另一个流解决方案:

    AtomicInteger balance = new AtomicInteger(0);
    int highestBalance = transactions
            .stream()
            .mapToInt(transaction -> {
                int amount = transaction.getAmount();
                if (transaction.getAction() == TransactionAction.WITHDRAW) {
                    amount = -amount;
                }
                return balance.accumulateAndGet(amount, Integer::sum);
            })
            .max()
            .orElse(0);

每个位置的累积和可以这样计算:

List integers=Arrays.asList(1200500,-700300,-800,-500);
Stream cumulativeSum=Stream.iterate(
新整数[]{0,integers.get(0)},
p->newinteger[]{p[0]+1,p[1]+integers.get(p[0]+1)}
)
.limit(integers.size());
通过这种方式,您可以通过以下方式获得最大平衡:

Integer[]max=cumulativeSum
.max(比较器比较(p->p[1]))
.get();
System.out.println(“位置:+max[0]);
System.out.println(“值:+max[1]);
或者使用
迭代器
,但这里有一个问题,即最后一个和不会被计算:

Stream<Integer> integerStream = Arrays.stream(new Integer[]{
        1200, 500, -700, 300, -800, -500});
Iterator<Integer> iterator = integerStream.iterator();
Integer maxCumulativeSum = Stream.iterate(iterator.next(), p -> p + iterator.next())
        .takeWhile(p -> iterator.hasNext())
        .max(Integer::compareTo).get();
System.out.println(maxCumulativeSum);
Stream integerStream=Arrays.Stream(新整数[]){
1200, 500, -700, 300, -800, -500});
迭代器迭代器=integerStream.Iterator();
整数maxCumulativeSum=Stream.iterate(iterator.next(),p->p+iterator.next())
.takeWhile(p->iterator.hasNext())
.max(整数::compareTo).get();
System.out.println(最大累积量);
问题在于,可以(从外部库)解决。

错误的解决方案
//存款为正,取款为负。
最终流TheoriginalDepositures取款=流(1200500,-700300,-800,-500);
最终流顺序存款提款=原始存款提款。顺序();
最终当前余额最大余额当前最大余额=顺序存款取款。减少(
//身份。
新的CurrentBalance MaximumBalance(0,整数。最小值),
//累加器。
(当前累计、元素存款提取)->{
最终整数新余额=
电流积累+
要素存款提取;
最终整数newMaximumBalance=Math.max(
电流累积。最大平衡,
新电流平衡
);
返回新的CurrentBalanceMaximumBalance(
newCurrentBalance,
新最大平衡
);
},
//组合器。
(res1,res2)->{
最终整数新余额=
res1.1当前余额+
res2.1电流平衡;
最终整数newMaximumBalance=Math.max(
res1.maximumBalance,
res2.maximumBalance
);
返回新的CurrentBalanceMaximumBalance(
newCurrentBalance,newMaximumBalance
);
}
);
System.out.println(“最大值为:“+currentMaximumBalance.maximumBalance”);
助手类:

class CurrentBalanceMaximumBalance{
公共收支平衡;
公共最终国际最大平衡;
公共电流平衡最大平衡(
int电流平衡,
内最大平衡
) {
此.currentBalance=currentBalance;
此项。最大平衡=最大平衡;
}
}
这是一个错误的解决方案。它可以任意工作,但不能保证它会工作

它破坏了
reduce
的界面。被破坏的属性是累加器函数和组合器函数的关联性。它也不要求流尊重原始事务的顺序

这使得使用可能很危险,并且可能会根据
reduce
的实现情况以及流是否尊重存款和取款的原始顺序给出错误的结果

在这里使用
sequential()
是不够的,因为
sequential()
是关于顺序/并行执行的。顺序执行但没有顺序的流的一个示例是从
哈希集
创建的流,然后对其调用
sequential()

正确的解决办法 该问题使用了“当前余额”的概念,并且只有在从第一笔交易开始计算,然后到最后计算时才有意义。例如,如果你有列表<代码> [-1000, 10, 10,-1000 ] < /代码>,你不能从中间开始,然后说“电流平衡”在某个点是20。您必须应用操作注册表。按原始交易顺序排列的“当前余额”

因此,一个直接的解决方案是:

  • 要求流尊重事务的原始顺序,并定义“遭遇”
    transactions = [1200, 500, -700, 300, -800, -500]
    csum = cumulativeSum(transactions) // should be [1200,1700,1000,1300,500,0]
    max(csum) // should be 1700
    
    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]