如何在Java中使用收集器聚合多个字段

时间:2019-10-04 02:35:20

标签: java java-stream collectors

我有一个自定义对象项,其中有两个字段金额和税额。我有一个Itemized对象的数组,我想对同一流中的两个字段求和。下面是我如何计算两个字段的总和。

double totalAmount = Arrays.stream(getCharges()).map(Itemized::getAmount).reduce(0.0, Double::sum));
double totalTax = Arrays.stream(getCharges()).map(Itemized::getTax).reduce(0.0, Double::sum));

有什么办法不必两次解析流,并且可以一次性将两个字段相加?我不希望对totalTax和totalAmount求和,但要分别求和。我当时在看收集器,但找不到任何示例可以一次汇总多个字段。

6 个答案:

答案 0 :(得分:4)

使用for循环?

double taxSum = 0;
double amountSum = 0;
for (Itemized itemized : getCharges()) {
    taxSum += itemized.getTax();
    amountSum += itemized.getAmount();
}

答案 1 :(得分:1)

您可以尝试使用T型收集器,如下所示:

Arrays.stream(getCharges())                                // Get the charges as a stream
    .collect(Collectors                                    // collect
        .teeing(                                           // both of the following:
            Collectors.summingDouble(Itemized::getAmount), //     first, the amounts
            Collectors.summingDouble(Itemized::getTax),    //     second, the sums
            Map::entry                                     // and combine them as an Entry
        )
    );

这应该为您提供一个Map.Entry<Double, Double>,以金额总和作为键,并以税收总和作为值,您可以提取。

修改:
测试并编译它-它可以工作。我们开始:

  

ItemizedTest.java

public class ItemizedTest
{
    static Itemized[] getCharges()
    {
        // sums should be first param = 30.6, second param = 75
        return new Itemized[] { new Itemized(10, 20), new Itemized(10.4,22), new Itemized(10.2, 33) };
    }

    public static void main(String[] args)
    {
        Map.Entry<Double, Double> sums = Arrays.stream(getCharges())
        .collect(Collectors
            .teeing(
                Collectors.summingDouble(Itemized::getAmount), 
                Collectors.summingDouble(Itemized::getTax),
                Map::entry
            )
        );
        System.out.println("sum of amounts: "+sums.getKey());
        System.out.println("sum of tax: "+sums.getValue());
    }
}
  

Itemized.java

public final class Itemized
{
    final double amount;
    final double tax;

    public double getAmount()
    {
        return amount;
    }

    public double getTax()
    {
        return tax;
    }

    public Itemized(double amount, double tax)
    {
        super();
        this.amount = amount;
        this.tax = tax;
    }
}

输出:

  

总金额:30.6
  税金:75.0

P.S .: teeing收集器仅在Java 12+中可用。

答案 2 :(得分:0)

您可以定义一个自定义对象来保存两个字段的和值,而不是按字段求和:

def get_formula(user_input):
  formula = None
  if user_input == 1:
    formula = lambda x: x['A'] + x['B']
  elif user_input == 2:
    formula = lambda x: x['A']**2 - x['B']
  elif user_input == 3:
    # Your conditions for user inputs 1 or 3 seem to be the same.
    formula = lambda x: x['A'] + x['B']
  else:
    # Error out
    pass
  return formula

df = pd.DataFrame({'A': list(range(5)), 'B': list(range(5))})

# Get user input (assumes it's an integer)
user_input = int(input('Enter formula #: '))
# Get formula based on input
fn = get_formula(user_input)
# Assign new column 'C' based on formula
df.assign(C=fn)

# Assuming user input is 1, this outputs:
   A  B  C
0  0  0  0
1  1  1  2
2  2  2  4
3  3  3  6
4  4  4  8

答案 3 :(得分:0)

使用一种数据结构,该数据结构可以使两个累加和求和,您可以将流简化为单个对象。

这是使用const TWEEN = require('@tweenjs/tween.js'); // Can probably be substituted with: import * as TWEEN from '@tweenjs/tweenjs' TWEEN.update(currentTime) double[]保持在索引0,将totalAmount保持在索引1(其他选项包括totalTaxSimpleEntry):

Pair

答案 4 :(得分:0)

您可以使用Entry来做到这一点,但最终您仍然会创建很多对象,我建议的最佳解决方案是NimChimpsky回答的for loop

Entry<Double, Double> entry = Arrays.stream(new Itemized[] {i1,i2})
          .map(it->new AbstractMap.SimpleEntry<>(it.getAmount(), it.getTax()))
          .reduce(new AbstractMap.SimpleEntry<>(0.0,0.0),
                  (a,b)->new AbstractMap.SimpleEntry<>(a.getKey()+b.getKey(),a.getValue()+b.getValue()));

    System.out.println("Amount : "+entry.getKey());
    System.out.println("Tax : "+entry.getValue());

答案 5 :(得分:0)

在您的特定情况下,可以通过使用Itemized类作为值持有者来完成。

Itemized result = Arrays.stream(getCharges())
    .reduce(new Itemized(), (acc, item) -> {
      acc.setAmount(acc.getAmount() + item.getAmount());
      acc.setTax(acc.getTax() + item.getTax());
      return acc;
    });
double totalAmount = result.getAmount();
double totalTax = result.getTax();