Javaでお金のために使用するデータ型は何ですか?[閉まっている]


183

あなたはJavaでお金のためにどのデータ型を使うべきですか?


2
実行する操作によって異なります。詳細をお知らせください。
Eversor

@eversorさまざまな操作に使用する必要のあるデータ型について説明してもらえますか?
クエストボーン

1
セントを正確に表す必要がある計算を行っています。
クエストボーン

アプリが処理する必要のある最大の金額を予測できますか?そして、あなたの計算は、それらは単純な(追加など)またはより複雑な金融操作になるでしょうか?
Eversor

回答:


133

JavaにはCurrency、ISO 4217通貨コードを表すクラスがあります。 BigDecimal通貨の10進数値を表すのに最適なタイプです。

Joda Moneyは、お金を表すライブラリを提供しています。


5
代わりにfloatまたはdoubleを使用できないのはなぜですか?
Erran Morad、2014

20
@Borat Sagdiyev これが理由です。また、あなたはを参照することができ、この
ブハケシンディ2014

2
@Borat:自分が何をしているかわかっている場合は、Peter Lawreyによるこの記事を参照してください。しかし、少なくともBigDecimalsを使用するのと同じくらい、すべての丸めを行うのは面倒です。
Nathan Hughes

35
「誰かがFLOATを使って通貨を保存しているのを見たとき、いつでも10セントあれば、Bill Karwin
Collin Krawll

36

Money and Currency API(JSR 354)を使用できます。プロジェクトに適切な依存関係を追加すれば、このAPIを使用できます。

Java 8の場合、次の参照実装を依存関係としてに追加しますpom.xml

<dependency>
    <groupId>org.javamoney</groupId>
    <artifactId>moneta</artifactId>
    <version>1.0</version>
</dependency>

この依存関係はjavax.money:money-api、依存関係として推移的に追加されます。

その後、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") );
    }
}

シリアル化とdbへの保存はどうですか?有線送信にはどのフォーマットを使用する必要がありますか?
パヴェルSzczur

1
私は、OracleがJava 9のJava Moneyを含めて熱心に取り組んだと思います。本当に残念です。しかし、すばらしい答えです。Mavenで引き続き使用できます
ボルジャブ2016年

3
Java 9にJava Moneyを含めることを反対するオラクルのソースはありますか?
Abdull、2016年

25

可能な最小値を表す整数型。つまり、プログラムはドル/ユーロではなくセントで考える必要があります。

これにより、GUIでドル/ユーロに戻すのを止めることはできません。


お金の量はint型のサイズをオーバーフローすることができますことを心に留めて
eversor

5
2,000万ドルを超える@eversorを必要とするほとんどのアプリは、政府機関でさえそれをオーバーフローさせるのに十分な量を処理しないので、十分な時間があれば、それほど必要ありません
ラチェットフリーク

4
@ratchetfreakおそらく長い時間を使う方が良いでしょう。
trognanders 2014年

5
多くの銀行は、毎日2,000万ドルというはるかに大きな金額を処理しています。これは、ドルの為替レートが高い円などの通貨も考慮していません。整数型は、金利や為替レートの計算が煩雑になりますが、丸めの問題を回避するのに最適です。ただし、アプリケーションによっては、64ビット整数型が必要になる場合があります。
アルキミスト2015年

理想的には、マイクロドル、実際、たとえば$ 10/3を行う場合、丸め誤差(3333.3 => 3333.0)は最終値にそれほど影響しません(この場合、実際の値にはまったく影響しませんが、絶対にしないと想定するのは危険です)。丸め誤差が大きくなるため、ユーザーが結果を表示する前に多数の計算を続けて行う場合、これは特に重要です。
Chris Browne


11

JSR 354:Money and Currency API

JSR 354は、MoneyとCurrencyを使用して包括的な計算を表現、転送、および実行するためのAPIを提供します。次のリンクからダウンロードできます。

JSR 354:Money and Currency APIダウンロード

仕様は次のもので構成されています。

  1. 金額や通貨などを処理するためのAPI
  2. 互換性のある実装をサポートするAPI
  3. 実装クラスのインスタンスを作成するためのファクトリー
  4. 金額の計算、変換、およびフォーマットの機能
  5. Money 9に含まれる予定のMoneyと通貨を操作するためのJava API。
  6. すべての仕様クラスとインターフェースは、javax.money。*パッケージにあります。

JSR 354のサンプル例:Money and Currency API:

MonetaryAmountを作成してコンソールに出力する例は次のようになります::

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

リファレンス実装APIを使用する場合、必要なコードははるかに単純です。

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

APIは、MonetaryAmountsによる計算もサポートしています。

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

CurrencyUnitとMonetaryAmount

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

MonetaryAmountには、割り当てられた通貨、数値、その精度などにアクセスできるさまざまなメソッドがあります。

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は、丸め演算子を使用して丸めることができます。

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

MonetaryAmountsのコレクションを操作する場合、フィルタリング、並べ替え、およびグループ化に役立つユーティリティメソッドがいくつか利用できます。

List<MonetaryAmount> 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"));

カスタムのMonetaryAmount操作

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

リソース:

JSR 354を使用してJavaでお金と通貨を処理する

Java 9 Money and Currency API(JSR 354)の調査

参照:JSR 354-通貨とお金


これはすべて良いですが、Federicoが上で示唆したように、BigDecimalよりも遅く見えます:-))悪い冗談はそれだけですが、1年後にテストします...
ケンサイ

6

BigDecimalを使用する必要があります 金額を表すにをます。これにより、さまざまな丸めモードを使用できるようになり、金融アプリケーションでは、丸めモードは多くの場合、法律で義務付けられるハード要件です。



6

パフォーマンスの点で、Moneta(Java通貨JSR 354実装)をBigDecimalと比較するためにマイクロベンチマーク(JMH)を実行しました。

驚いたことに、BigDecimalのパフォーマンスは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);
    }
}

その結果

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

何か足りない場合は、遠慮なく訂正してください


興味深いことに、JDK9で最新のものを使用して同じテストを実行します
ケンサイ2017

4

単純なケース(1つの通貨)の場合は十分ですInteger/ Long。セント(...)または100分の1/1000分の1セント(固定除算器で必要な任意の精度)でお金を維持します。


3

BigDecimalは、通貨に使用するのに最適なデータ型です。

通貨用のコンテナはたくさんありますが、それらはすべて、基になるデータ型としてBigDecimalを使用します。BigDecimal.ROUND_HALF_EVEN丸めを使用して、BigDecimalで問題が発生することはありません。


2

タイニータイプを使うのが好き以前の回答が示唆していたように、double、BigDecimal、またはintのいずれかをラップをです。(精度の問題が発生しない限り、私はdoubleを使用します)。

タイニータイプはタイプセーフを提供するので、ダブルマネーを他のダブルと混同しないでください。


6
私も小さなタイプが好きですが、金額を格納するためにdoubleを使用しないでください
オリエン
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.