धाराओं का उपयोग करके BigDecimals जोड़ना


178

मेरे पास BigDecimals (इस उदाहरण में, क LinkedList) का संग्रह है जिसे मैं एक साथ जोड़ना चाहूंगा। क्या इसके लिए धाराओं का उपयोग करना संभव है?

मैंने देखा कि Streamकक्षा में कई विधियाँ हैं

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

जिनमें से प्रत्येक में एक सुविधाजनक sum()विधि है। लेकिन, जैसा कि हम जानते हैं, floatऔर doubleअंकगणित लगभग हमेशा एक बुरा विचार है।

तो, क्या बिगडाइमिकल्स को योग करने का एक सुविधाजनक तरीका है?

यह मेरे पास अब तक का कोड है।

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

जैसा कि आप देख सकते हैं, मैं BigDecimals का उपयोग करके सारांशित कर रहा हूं BigDecimal::doubleValue(), लेकिन यह (जैसा कि अपेक्षित है) सटीक नहीं है।

उत्तर-उत्तरोत्तर संपादन के लिए:

दोनों जवाब बेहद मददगार थे। मैं थोड़ा जोड़ना चाहता था: मेरे वास्तविक जीवन के परिदृश्य में कच्चे BigDecimalएस का संग्रह शामिल नहीं है , वे एक चालान में लिपटे हुए हैं। लेकिन, मैं map()स्ट्रीम के लिए फ़ंक्शन का उपयोग करके इसके लिए अमन अग्निहोत्री के जवाब को संशोधित करने में सक्षम था :

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. कम करने की विधि को बुलाओ।

    3.1। हम इसके अलावा, के लिए एक पहचान मूल्य की आपूर्ति करते हैं BigDecimal.ZERO

    3.2। हम निर्दिष्ट करते हैं BinaryOperator<BigDecimal>, जो BigDecimalएक विधि संदर्भ के माध्यम से दो को जोड़ता है BigDecimal::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>और फिर इसे कम करता हूं BigDecimal

अब, एक OOP डिज़ाइन बिंदु से मैं आपको सलाह दूंगा कि आप वास्तव में उस total()विधि का उपयोग करें , जिसे आपने पहले ही परिभाषित कर लिया है, फिर यह और भी आसान हो जाता है:

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

यहां हम सीधे विधि संदर्भ का उपयोग विधि में करते हैं map


12
Invoice::totalबनाम के लिए +1 invoice -> invoice.total()
ryvantage

12
+1 विधि संदर्भों के लिए और धारा संचालन के बीच लाइन ब्रेक जोड़ने के लिए, दोनों IMHO पठनीयता में काफी सुधार करते हैं।
स्टुअर्ट मार्क्स

यह कैसे काम करेगा अगर मैं जोड़ना चाहता हूं कि इनवॉयस :: कुल और इनवॉइस :: टैक्स को एक नए सरणी में कहें
रिचर्ड लाउ नौ

जावा मानक पुस्तकालय में पहले से ही पूर्णांक / युगल जैसे योग हैं Collectors.summingInt(), लेकिन BigDecimalएस के लिए उन्हें याद करता है । लिखने के बजाय जो reduce(blah blah blah)पढ़ना मुश्किल है, उसके लिए लापता कलेक्टर को लिखना बेहतर होगा BigDecimalऔर .collect(summingBigDecimal())आपकी पाइपलाइन के अंत में होगा।
सीएसरफॉल्क

2
यह दृष्टिकोण NullponterException
gstackoverflow

11

इस पोस्ट में पहले से ही एक चेक किया हुआ उत्तर है, लेकिन उत्तर शून्य मानों के लिए फ़िल्टर नहीं करता है। सही उत्तर को ऑब्जेक्ट :: 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);

यह शून्य मानों को कम करने के प्रयास के रूप में अभिव्यक्त होने से रोकता है।


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()जरूरत नहीं है।
रोहित जैन

@ रोहितजैन: अपडेट किया गया। धन्यवाद। मैंने इसका उपयोग किया get()क्योंकि यह कॉल Optionalद्वारा लौटाए गए मूल्य को लौटाता है reduce। यदि कोई काम करना चाहता है Optionalया सिर्फ राशि का प्रिंट आउट लेता है, तो हाँ, get()इसकी आवश्यकता नहीं है। लेकिन वैकल्पिक सीधे Optional[<Value>]सिंटैक्स आधारित सिंटैक्स प्रिंट करना जो मुझे संदेह है कि उपयोगकर्ता की आवश्यकता होगी। तो get()एक तरह से मूल्य प्राप्त करने की जरूरत है Optional
अमन अग्निहोत्री

@ गुरुभक्त: हाँ, आपका दृष्टिकोण ठीक है कि मैंने इसे कैसे किया होगा। :)
अमन अग्निहोत्री

बिना शर्त getकॉल का उपयोग न करें! यदि valuesएक खाली सूची है तो वैकल्पिक का कोई मूल्य नहीं होगा और NoSuchElementExceptionजब getकहा जाता है तो फेंक देगा । आप values.stream().reduce(BigDecimal::add).orElse(BigDecimal.ZERO)इसके बजाय उपयोग कर सकते हैं ।
ईई

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());

नोट: मैं एक्लिप्स कलेक्शंस के लिए कमिट हूं।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.