스트림을 사용하여 BigDecimals 추가


178

BigDecimals 모음이 있습니다 (이 예에서는 LinkedList함께 추가하려는 ) 있습니다. 이를 위해 스트림을 사용할 수 있습니까?

Stream수업에 몇 가지 방법이 있다는 것을 알았습니다.

Stream::mapToInt
Stream::mapToDouble
Stream::mapToLong

각각 편리한 sum()방법이 있습니다. 우리가 알다시피하지만, floatdouble연산은 거의 항상 나쁜 생각이다.

BigDecimals를 요약하는 편리한 방법이 있습니까?

이것은 지금까지 가지고있는 코드입니다.

public static void main(String[] args) {
    LinkedList<BigDecimal> values = new LinkedList<>();
    values.add(BigDecimal.valueOf(.1));
    values.add(BigDecimal.valueOf(1.1));
    values.add(BigDecimal.valueOf(2.1));
    values.add(BigDecimal.valueOf(.1));

    // Classical Java approach
    BigDecimal sum = BigDecimal.ZERO;
    for(BigDecimal value : values) {
        System.out.println(value);
        sum = sum.add(value);
    }
    System.out.println("Sum = " + sum);

    // Java 8 approach
    values.forEach((value) -> System.out.println(value));
    System.out.println("Sum = " + values.stream().mapToDouble(BigDecimal::doubleValue).sum());
    System.out.println(values.stream().mapToDouble(BigDecimal::doubleValue).summaryStatistics().toString());
}

보시다시피, 나는을 사용하여 BigDecimal을 요약하고 BigDecimal::doubleValue()있지만 이것은 예상대로 정확하지 않습니다.

후손에 대한 답변 후 수정 :

두 답변 모두 매우 도움이되었습니다. 나는 약간을 추가하고 싶었다 : 나의 실제 시나리오는 raw 컬렉션을 포함하지 않으며 BigDecimal, 그것들은 송장에 싸여있다. 그러나 map()스트림 기능을 사용하여 Aman Agnihotri의 대답을 설명하기 위해 Aman Agnihotri의 답변을 수정할 수있었습니다 .

public static void main(String[] args) {

    LinkedList<Invoice> invoices = new LinkedList<>();
    invoices.add(new Invoice("C1", "I-001", BigDecimal.valueOf(.1), BigDecimal.valueOf(10)));
    invoices.add(new Invoice("C2", "I-002", BigDecimal.valueOf(.7), BigDecimal.valueOf(13)));
    invoices.add(new Invoice("C3", "I-003", BigDecimal.valueOf(2.3), BigDecimal.valueOf(8)));
    invoices.add(new Invoice("C4", "I-004", BigDecimal.valueOf(1.2), BigDecimal.valueOf(7)));

    // Classical Java approach
    BigDecimal sum = BigDecimal.ZERO;
    for(Invoice invoice : invoices) {
        BigDecimal total = invoice.unit_price.multiply(invoice.quantity);
        System.out.println(total);
        sum = sum.add(total);
    }
    System.out.println("Sum = " + sum);

    // Java 8 approach
    invoices.forEach((invoice) -> System.out.println(invoice.total()));
    System.out.println("Sum = " + invoices.stream().map((x) -> x.total()).reduce((x, y) -> x.add(y)).get());
}

static class Invoice {
    String company;
    String invoice_number;
    BigDecimal unit_price;
    BigDecimal quantity;

    public Invoice() {
        unit_price = BigDecimal.ZERO;
        quantity = BigDecimal.ZERO;
    }

    public Invoice(String company, String invoice_number, BigDecimal unit_price, BigDecimal quantity) {
        this.company = company;
        this.invoice_number = invoice_number;
        this.unit_price = unit_price;
        this.quantity = quantity;
    }

    public BigDecimal total() {
        return unit_price.multiply(quantity);
    }

    public void setUnit_price(BigDecimal unit_price) {
        this.unit_price = unit_price;
    }

    public void setQuantity(BigDecimal quantity) {
        this.quantity = quantity;
    }

    public void setInvoice_number(String invoice_number) {
        this.invoice_number = invoice_number;
    }

    public void setCompany(String company) {
        this.company = company;
    }

    public BigDecimal getUnit_price() {
        return unit_price;
    }

    public BigDecimal getQuantity() {
        return quantity;
    }

    public String getInvoice_number() {
        return invoice_number;
    }

    public String getCompany() {
        return company;
    }
}

답변:


354

원래 답변

예, 가능합니다 :

List<BigDecimal> bdList = new ArrayList<>();
//populate list
BigDecimal result = bdList.stream()
        .reduce(BigDecimal.ZERO, BigDecimal::add);

그것이하는 일은 :

  1. 를 얻습니다 List<BigDecimal>.
  2. 그것을 Stream<BigDecimal>
  3. reduce 메소드를 호출하십시오.

    3.1. 우리는 추가를 위해 즉,BigDecimal.ZERO .

    3.2. 메소드 참조를 통해 BinaryOperator<BigDecimal>2를 더하는를 지정합니다 .BigDecimalBigDecimal::add

편집 후 업데이트 된 답변

새 데이터를 추가 했으므로 새 답변은 다음과 같습니다.

List<Invoice> invoiceList = new ArrayList<>();
//populate
Function<Invoice, BigDecimal> totalMapper = invoice -> invoice.getUnit_price().multiply(invoice.getQuantity());
BigDecimal result = invoiceList.stream()
        .map(totalMapper)
        .reduce(BigDecimal.ZERO, BigDecimal::add);

totalMapper변수를 추가했다는 점을 제외하고는 거의 동일 Invoice합니다.BigDecimal 그 송장의 총 가격을 반환합니다.

그런 다음을 얻고에 Stream<Invoice>매핑 Stream<BigDecimal>한 다음 a 로 줄입니다.BigDecimal 입니다.

이제 OOP 디자인 포인트에서 total()이미 정의한 방법을 실제로 사용하는 것이 좋습니다.

List<Invoice> invoiceList = new ArrayList<>();
//populate
BigDecimal result = invoiceList.stream()
        .map(Invoice::total)
        .reduce(BigDecimal.ZERO, BigDecimal::add);

여기서는 메소드의 메소드 참조를 직접 사용합니다 map.


12
+1 Invoice::totalinvoice -> invoice.total().
ryvantage

12
스트림 참조 사이에 메소드 참조 및 줄 바꿈을 추가하는 경우 +1. IMHO는 가독성을 크게 향상시킵니다.
스튜어트 마크

추가하려는 경우 어떻게 작동합니까? Invoice :: total 및 Invoice :: tax를 새 배열에 추가 할 수 있습니다.
Richard Lau

Java 표준 라이브러리에는 이미 정수 / 더블을 합산하는 기능이 Collectors.summingInt()있지만 BigDecimals 에서는 누락됩니다 . 대신 작성 reduce(blah blah blah)을 위해 수집 누락 쓰기 좋을 것 읽어 어려운 것을 BigDecimal하고있는 .collect(summingBigDecimal())파이프 라인의 끝에서.
csharpfolk

2
이 방법은 NullponterException으로 이어질 수
gstackoverflow

11

이 게시물에는 이미 확인 된 답변이 있지만 해당 답변은 null 값을 필터링하지 않습니다. 정답은 Object :: nonNull 함수를 술어로 사용하여 널값을 방지해야합니다.

BigDecimal result = invoiceList.stream()
    .map(Invoice::total)
    .filter(Objects::nonNull)
    .filter(i -> (i.getUnit_price() != null) && (i.getQuantity != null))
    .reduce(BigDecimal.ZERO, BigDecimal::add);

이렇게하면 줄어든 null 값을 합치려고하지 않습니다.


7

당신은의 값 요약 할 수 BigDecimal사용하여 스트림 재사용 수집기 라는 이름을 summingUp:

BigDecimal sum = bigDecimalStream.collect(summingUp());

다음 Collector과 같이 구현할 수 있습니다.

public static Collector<BigDecimal, ?, BigDecimal> summingUp() {
    return Collectors.reducing(BigDecimal.ZERO, BigDecimal::add);
}

5

이 방법을 사용하여 BigDecimal 목록을 합산하십시오.

List<BigDecimal> values = ... // List of BigDecimal objects
BigDecimal sum = values.stream().reduce((x, y) -> x.add(y)).get();

이 접근법은 각 BigDecimal을 BigDecimal로만 맵핑하고이를 합산하여 축소 한 다음 get()메소드를 사용하여 리턴합니다 .

동일한 합산을 수행하는 또 다른 간단한 방법이 있습니다.

List<BigDecimal> values = ... // List of BigDecimal objects
BigDecimal sum = values.stream().reduce(BigDecimal::add).get();

최신 정보

편집 된 질문에 클래스와 람다 식을 쓰려면 다음과 같이 작성했을 것입니다.

import java.math.BigDecimal;
import java.util.LinkedList;

public class Demo
{
  public static void main(String[] args)
  {
    LinkedList<Invoice> invoices = new LinkedList<>();
    invoices.add(new Invoice("C1", "I-001", BigDecimal.valueOf(.1), BigDecimal.valueOf(10)));
    invoices.add(new Invoice("C2", "I-002", BigDecimal.valueOf(.7), BigDecimal.valueOf(13)));
    invoices.add(new Invoice("C3", "I-003", BigDecimal.valueOf(2.3), BigDecimal.valueOf(8)));
    invoices.add(new Invoice("C4", "I-004", BigDecimal.valueOf(1.2), BigDecimal.valueOf(7)));

    // Java 8 approach, using Method Reference for mapping purposes.
    invoices.stream().map(Invoice::total).forEach(System.out::println);
    System.out.println("Sum = " + invoices.stream().map(Invoice::total).reduce((x, y) -> x.add(y)).get());
  }

  // This is just my style of writing classes. Yours can differ.
  static class Invoice
  {
    private String company;
    private String number;
    private BigDecimal unitPrice;
    private BigDecimal quantity;

    public Invoice()
    {
      unitPrice = quantity = BigDecimal.ZERO;
    }

    public Invoice(String company, String number, BigDecimal unitPrice, BigDecimal quantity)
    {
      setCompany(company);
      setNumber(number);
      setUnitPrice(unitPrice);
      setQuantity(quantity);
    }

    public BigDecimal total()
    {
      return unitPrice.multiply(quantity);
    }

    public String getCompany()
    {
      return company;
    }

    public void setCompany(String company)
    {
      this.company = company;
    }

    public String getNumber()
    {
      return number;
    }

    public void setNumber(String number)
    {
      this.number = number;
    }

    public BigDecimal getUnitPrice()
    {
      return unitPrice;
    }

    public void setUnitPrice(BigDecimal unitPrice)
    {
      this.unitPrice = unitPrice;
    }

    public BigDecimal getQuantity()
    {
      return quantity;
    }

    public void setQuantity(BigDecimal quantity)
    {
      this.quantity = quantity;
    }
  }
}

하지가 .map(n -> n)쓸모가? 또한 get()필요하지 않습니다.
Rohit Jain

@RohitJain : 업데이트되었습니다. 감사. 호출에 의해 get()반환되는 값을 반환하므로 사용 했습니다 . 하나를 사용 하거나 합계를 인쇄하려면 필요하지 않습니다. 그러나 Optional을 인쇄 하면 사용자가 필요로하는 의심스러운 구문을 직접 인쇄 합니다. 따라서 에서 값을 얻는 방법이 필요합니다 . OptionalreduceOptionalget()Optional[<Value>]get()Optional
Aman Agnihotri

@ryvantage : 네, 당신의 접근 방식은 내가했던 방식과 정확히 같습니다. :)
Aman Agnihotri

무조건 get전화를 사용하지 마십시오 ! 경우 values빈 목록입니다 옵션은 어떤 값을 포함하지 않습니다과를 던질 것이다 NoSuchElementExceptionget호출된다. values.stream().reduce(BigDecimal::add).orElse(BigDecimal.ZERO)대신 사용할 수 있습니다 .
eee

4

당신은 타사 의존성을 신경 쓰지 않으면라는 이름의 클래스가 Collectors2 에서 이클립스 컬렉션 방법에 대한 수집을 반환 포함 합산요약 BigDecimal와 BigInteger를. 이러한 메소드는 함수 를 매개 변수로 사용하므로 오브젝트에서 BigDecimal 또는 BigInteger 값을 추출 할 수 있습니다.

List<BigDecimal> list = mList(
        BigDecimal.valueOf(0.1),
        BigDecimal.valueOf(1.1),
        BigDecimal.valueOf(2.1),
        BigDecimal.valueOf(0.1));

BigDecimal sum =
        list.stream().collect(Collectors2.summingBigDecimal(e -> e));
Assert.assertEquals(BigDecimal.valueOf(3.4), sum);

BigDecimalSummaryStatistics statistics =
        list.stream().collect(Collectors2.summarizingBigDecimal(e -> e));
Assert.assertEquals(BigDecimal.valueOf(3.4), statistics.getSum());
Assert.assertEquals(BigDecimal.valueOf(0.1), statistics.getMin());
Assert.assertEquals(BigDecimal.valueOf(2.1), statistics.getMax());
Assert.assertEquals(BigDecimal.valueOf(0.85), statistics.getAverage());

참고 : 저는 Eclipse Collections의 커미터입니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.