Qual è il miglior tipo di dati da utilizzare per l’app Java?

Qual è il miglior tipo di dati da utilizzare per soldi nell’applicazione Java?

    Java ha class Currency che rappresenta i codici di valuta ISO 4217. BigDecimal è il tipo migliore per rappresentare i valori decimali della valuta.

    Joda Money ha fornito una biblioteca per rappresentare i soldi.

    È ansible utilizzare l’ API Money and Currency (JSR 354) . Questa API dovrebbe essere parte di Java 9. È ansible utilizzare questa API in Java 7 e Java 8, a condizione di aggiungere dipendenze appropriate al progetto.

    Per Java 8, aggiungi la seguente implementazione di riferimento come dipendenza al tuo pom.xml :

      org.javamoney moneta 1.0  

    Questa dipendenza aggiungerà transitivamente javax.money:money-api come dipendenza.

    Puoi quindi utilizzare l’API:

     package com.example.money; import static org.junit.Assert.assertThat; import static org.hamcrest.CoreMatchers.is; import java.util.Locale; import javax.money.Monetary; import javax.money.MonetaryAmount; import javax.money.MonetaryRounding; import javax.money.format.MonetaryAmountFormat; import javax.money.format.MonetaryFormats; import org.junit.Test; public class MoneyTest { @Test public void testMoneyApi() { MonetaryAmount eurAmount1 = Monetary.getDefaultAmountFactory().setNumber(1.1111).setCurrency("EUR").create(); MonetaryAmount eurAmount2 = Monetary.getDefaultAmountFactory().setNumber(1.1141).setCurrency("EUR").create(); MonetaryAmount eurAmount3 = eurAmount1.add(eurAmount2); assertThat(eurAmount3.toString(), is("EUR 2.2252")); MonetaryRounding defaultRounding = Monetary.getDefaultRounding(); MonetaryAmount eurAmount4 = eurAmount3.with(defaultRounding); assertThat(eurAmount4.toString(), is("EUR 2.23")); MonetaryAmountFormat germanFormat = MonetaryFormats.getAmountFormat(Locale.GERMAN); assertThat(germanFormat.format(eurAmount4), is("EUR 2,23") ); } } 

    Un tipo integrale che rappresenta il minimo valore ansible. In altre parole, il tuo programma dovrebbe pensare in centesimi, non in dollari / euro.

    Questo non dovrebbe impedirti di tradurlo in dollari / euro.

    BigDecimal può essere utilizzato, una buona spiegazione del perché non utilizzare Float o Double può essere visto qui: Perché non usare Double o Float per rappresentare la valuta?

    JSR 354: API Money and Currency

    JSR 354 fornisce un’API per la rappresentazione, il trasporto e l’esecuzione di calcoli completi con denaro e valuta. Puoi scaricarlo da questo link:

    JSR 354: download dell’API Money and Currency

    La specifica consiste delle seguenti cose:

    1. Un’API per la gestione, ad esempio, di importi e valute monetarie
    2. API per supportare implementazioni intercambiabili
    3. Fabbriche per la creazione di istanze delle classi di implementazione
    4. Funzionalità per calcoli, conversione e formattazione di importi monetari
    5. API Java per lavorare con denaro e valute, che è prevista per essere inclusa in Java 9.
    6. Tutte le classi di specifiche e le interfacce si trovano nel pacchetto javax.money. *.

    Esempi di esempio di JSR 354: API di denaro e valuta:

    Un esempio di creazione di MonetaryAmount e la stampa sulla console è simile a questo:

     MonetaryAmountFactory< ?> amountFactory = Monetary.getDefaultAmountFactory(); MonetaryAmount monetaryAmount = amountFactory.setCurrency(Monetary.getCurrency("EUR")).setNumber(12345.67).create(); MonetaryAmountFormat format = MonetaryFormats.getAmountFormat(Locale.getDefault()); System.out.println(format.format(monetaryAmount)); 

    Quando si utilizza l’API di implementazione di riferimento, il codice necessario è molto più semplice:

     MonetaryAmount monetaryAmount = Money.of(12345.67, "EUR"); MonetaryAmountFormat format = MonetaryFormats.getAmountFormat(Locale.getDefault()); System.out.println(format.format(monetaryAmount)); 

    L’API supporta anche i calcoli con MonetaryAmounts:

     MonetaryAmount monetaryAmount = Money.of(12345.67, "EUR"); MonetaryAmount otherMonetaryAmount = monetaryAmount.divide(2).add(Money.of(5, "EUR")); 

    CurrencyUnit e MonetaryAmount

     // getting CurrencyUnits by locale CurrencyUnit yen = MonetaryCurrencies.getCurrency(Locale.JAPAN); CurrencyUnit canadianDollar = MonetaryCurrencies.getCurrency(Locale.CANADA); 

    MonetaryAmount ha vari metodi che consentono di accedere alla valuta assegnata, alla quantità numerica, alla sua precisione e altro:

     MonetaryAmount monetaryAmount = Money.of(123.45, euro); CurrencyUnit currency = monetaryAmount.getCurrency(); NumberValue numberValue = monetaryAmount.getNumber(); int intValue = numberValue.intValue(); // 123 double doubleValue = numberValue.doubleValue(); // 123.45 long fractionDenominator = numberValue.getAmountFractionDenominator(); // 100 long fractionNumerator = numberValue.getAmountFractionNumerator(); // 45 int precision = numberValue.getPrecision(); // 5 // NumberValue extends java.lang.Number. // So we assign numberValue to a variable of type Number Number number = numberValue; 

    MonetaryAmounts può essere arrotondato utilizzando un operatore di arrotondamento:

     CurrencyUnit usd = MonetaryCurrencies.getCurrency("USD"); MonetaryAmount dollars = Money.of(12.34567, usd); MonetaryOperator roundingOperator = MonetaryRoundings.getRounding(usd); MonetaryAmount roundedDollars = dollars.with(roundingOperator); // USD 12.35 

    Quando si lavora con le raccolte di MonetaryAmounts, sono disponibili alcuni utili metodi di utilità per il filtraggio, l’ordinamento e il raggruppamento.

     List amounts = new ArrayList<>(); amounts.add(Money.of(2, "EUR")); amounts.add(Money.of(42, "USD")); amounts.add(Money.of(7, "USD")); amounts.add(Money.of(13.37, "JPY")); amounts.add(Money.of(18, "USD")); 

    Operazioni di MonetaryAmount personalizzate

     // A monetary operator that returns 10% of the input MonetaryAmount // Implemented using Java 8 Lambdas MonetaryOperator tenPercentOperator = (MonetaryAmount amount) -> { BigDecimal baseAmount = amount.getNumber().numberValue(BigDecimal.class); BigDecimal tenPercent = baseAmount.multiply(new BigDecimal("0.1")); return Money.of(tenPercent, amount.getCurrency()); }; MonetaryAmount dollars = Money.of(12.34567, "USD"); // apply tenPercentOperator to MonetaryAmount MonetaryAmount tenPercentDollars = dollars.with(tenPercentOperator); // USD 1.234567 

    risorse:

    Gestione di denaro e valute in Java con JSR 354

    Esame dell’API Java 9 Money and Currency (JSR 354)

    Vedi anche: JSR 354 – Valuta e denaro

    Vorrei usare Joda Money

    È ancora alla versione 0.6 ma sembra molto promettente

    È necessario utilizzare BigDecimal per rappresentare i valori monetari. Consente di utilizzare una varietà di modalità di arrotondamento e, nelle applicazioni finanziarie, la modalità di arrotondamento è spesso un requisito difficile che può anche essere richiesto dalla legge.

    Ho eseguito un microbenchmark (JMH) per confrontare Moneta (implementazione JSR 354 della valuta java) rispetto a BigDecimal in termini di prestazioni.

    Sorprendentemente, le prestazioni BigDecimal sembrano essere migliori di quelle di moneta. Ho usato la seguente configurazione della moneta:

    org.javamoney.moneta.Money.defaults.precision = 19 org.javamoney.moneta.Money.defaults.roundingMode = HALF_UP

     package com.despegar.bookedia.money; import org.javamoney.moneta.FastMoney; import org.javamoney.moneta.Money; import org.openjdk.jmh.annotations.*; import java.math.BigDecimal; import java.math.MathContext; import java.math.RoundingMode; import java.util.concurrent.TimeUnit; @Measurement(batchSize = 5000, iterations = 10, time = 2, timeUnit = TimeUnit.SECONDS) @Warmup(iterations = 2) @Threads(value = 1) @Fork(value = 1) @State(Scope.Benchmark) @BenchmarkMode(Mode.Throughput) public class BigDecimalBenchmark { private static final Money MONEY_BASE = Money.of(1234567.3444, "EUR"); private static final Money MONEY_SUBSTRACT = Money.of(232323, "EUR"); private static final FastMoney FAST_MONEY_SUBSTRACT = FastMoney.of(232323, "EUR"); private static final FastMoney FAST_MONEY_BASE = FastMoney.of(1234567.3444, "EUR"); MathContext mc = new MathContext(10, RoundingMode.HALF_UP); @Benchmark public void bigdecimal_string() { new BigDecimal("1234567.3444").subtract(new BigDecimal("232323")).multiply(new BigDecimal("3.4"), mc).divide(new BigDecimal("5.456"), mc); } @Benchmark public void bigdecimal_valueOf() { BigDecimal.valueOf(12345673444L, 4).subtract(BigDecimal.valueOf(232323L)).multiply(BigDecimal.valueOf(34, 1), mc).divide(BigDecimal.valueOf(5456, 3), mc); } @Benchmark public void fastmoney() { FastMoney.of(1234567.3444, "EUR").subtract(FastMoney.of(232323, "EUR")).multiply(3.4).divide(5.456); } @Benchmark public void money() { Money.of(1234567.3444, "EUR").subtract(Money.of(232323, "EUR")).multiply(3.4).divide(5.456); } @Benchmark public void money_static(){ MONEY_BASE.subtract(MONEY_SUBSTRACT).multiply(3.4).divide(5.456); } @Benchmark public void fastmoney_static() { FAST_MONEY_BASE.subtract(FAST_MONEY_SUBSTRACT).multiply(3.4).divide(5.456); } } 

    Con il risultato di

     Benchmark Mode Cnt Score Error Units BigDecimalBenchmark.bigdecimal_string thrpt 10 479.465 ± 26.821 ops/s BigDecimalBenchmark.bigdecimal_valueOf thrpt 10 1066.754 ± 40.997 ops/s BigDecimalBenchmark.fastmoney thrpt 10 83.917 ± 4.612 ops/s BigDecimalBenchmark.fastmoney_static thrpt 10 504.676 ± 21.642 ops/s BigDecimalBenchmark.money thrpt 10 59.897 ± 3.061 ops/s BigDecimalBenchmark.money_static thrpt 10 184.767 ± 7.017 ops/s 

    Sentiti libero di correggermi se mi manca qualcosa

    Mi piace usare Tiny Types che avvolgere un doppio, BigDecimal o int come suggerito da precedenti risposte. (Vorrei usare una doppia se non emergono problemi di precisione).

    Un Tiny Type ti dà sicurezza del tipo in modo da non confondere un doppio denaro con altri doppi.

    BigDecimal è il miglior tipo di dati da utilizzare per la valuta.

    Esistono molti contenitori per la valuta, ma tutti usano BigDecimal come tipo di dati sottostante. Non sbaglierai con BigDecimal, probabilmente usando l’arrotondamento BigDecimal.ROUND_HALF_EVEN.

    Per caso semplice (una valuta) è abbastanza Integer / Long . Mantenere il denaro in centesimi (…) o centesimo / millesimo di centesimi (qualsiasi precisione necessaria con divisore fisso)