これは、2つのBigDecimal
オブジェクトを比較する方法に関する質問ではありません。compareTo
はequals
の代わりにequals
を使用して比較できることを知っています。文書化:
CompareToとは異なり、このメソッドは2つのBigDecimalオブジェクトが値とスケールが等しい場合にのみ等しいと見なします(したがって、このメソッドで比較すると、2.0は2.00と等しくありません)。
問題は、なぜequals
がこの一見逆に見える方法で指定されているのですか?つまり、2.0と2.00を区別できるのはなぜ重要なのでしょうか。
Comparable
メソッドを指定するcompareTo
のドキュメントには次のように記載されているため、これには理由があるはずです。
自然な順序付けが等しいと一致することを強くお勧めします(必須ではありません)
この推奨を無視するのには十分な理由があるに違いないと思います。
状況によっては、精度の表示(エラーのマージンなど)が重要になる場合があります。
たとえば、2つの物理センサーによって行われた測定を保存している場合、おそらく1つは他の10倍の精度です。この事実を表すことが重要かもしれません。
他のどの回答でもまだ考慮されていない点は、equals
がhashCode
と一貫している必要があることと、必要なhashCode
実装のコストです。 123.00と同じ値を123.0に生成すること(ただし、異なる値を区別するための合理的な仕事を行うこと)は、そうする必要のなかったhashCode実装の値よりもはるかに大きくなります。現在のセマンティクスでは、hashCode
には32の乗算が必要であり、32ビットの格納値ごとに加算します。 hashCode
が異なる精度の値間で一貫している必要がある場合は、任意の値(高価)の正規化された形式を計算するか、少なくとも、base-999999999のデジタルルートを計算するような処理を行う必要があります。値に基づいて、精度に基づいてmod 999999999を乗算します。このようなメソッドの内部ループは次のようになります。
temp = (temp + (mag[i] & LONG_MASK) * scale_factor[i]) % 999999999;
31による乗算を64ビットのモジュラス演算で置き換えると、はるかに高価になります。数値的に同等のBigDecimal
値を同等と見なすハッシュテーブルが必要で、テーブルで検索されるほとんどのキーが見つかる場合、目的の結果を得る効率的な方法は、次のようなハッシュテーブルを使用することです。値を直接格納するのではなく、値ラッパーを格納します。テーブル内の値を見つけるには、まず値自体を探します。何も見つからない場合は、値を正規化して探します。何も見つからない場合は、空のラッパーを作成し、元の正規化された形式の数値の下にエントリを保存します。
テーブルにないものや以前に検索されていないものを探すには、費用のかかる正規化手順が必要ですが、検索されたものを探すのははるかに高速です。対照的に、HashCodeが、精度が異なるためにまったく異なる方法で格納されている数値と同等の値を返す必要がある場合、すべてのハッシュテーブル操作が大幅に遅くなります。
数学では、10.0は10.00と同じです。物理学では、10.0mと10.00mは間違いなく異なります(精度は異なります)。OOPでオブジェクトについて話すとき、私は間違いなくそれらは等しくないと言います。
Equalsがスケールを無視した場合の予期しない機能について考えるのも簡単です(たとえば、a.equals(b)の場合、a.add(0.1).equals(b.add(0.1)?).
数値が丸められると、計算の精度が表示されます-言い換えると:
言い換えれば、それは 算術精度 にリンクされています。
この推奨を無視するのには十分な理由があるに違いないと思います。
そうでないかもしれない。 BigDecimal
の設計者が設計の選択を誤ったという簡単な説明を提案します。
Comparable
が等式と一致しているという規則に違反しています。興味深いことに、ScalaのBigDecimal
クラス(JavaのBigDecimal
を使用して実装されている)は、反対の選択をしています。
BigDecimal("2.0") == BigDecimal("2.00") // true
compareTo
メソッドは、末尾のゼロがBigDecimal
で表される数値に影響を与えないことを認識しています。これは、compareTo
が注意する唯一の側面です。対照的に、equals
メソッドは通常、誰かがオブジェクトのどの側面を気にしているかを知る方法がないため、2つのオブジェクトがeveryで同等である場合にのみtrue
を返す必要があります=プログラマーが興味を持つかもしれない方法。x.equals(y)
がtrueである場合、x.toString().equals(y.toString())
がfalseを返すのは意外です。
おそらくさらに重要なもう1つの問題は、BigDecimal
が基本的にBigInteger
とスケーリング係数を組み合わせているため、2つの数値が同じ値を表しているが後続のゼロの数が異なる場合、 a bigInteger
の値は、他の10の累乗です。等式で仮数とスケールの両方が一致する必要がある場合、BigDecimal
のhashCode()
はBigInteger
のハッシュコードを使用できます。ただし、2つの値が異なるBigInteger
値を含んでいても、「等しい」と見なされる可能性がある場合は、事態が非常に複雑になります。 BigDecimal
ではなく、独自のバッキングストレージを使用するBigInteger
型をさまざまな方法で実装して、同じ数値を表す値がすばやくハッシュされるように数値をハッシュすることができます。を比較します(簡単な例として、各long
値に9桁の10進数をパックし、小数点が9のグループの間にあることを常に要求するバージョンでは、末尾を無視する方法でハッシュコードを計算できます値がゼロのグループ)ですが、BigDecimal
をカプセル化するBigInteger
はそれを行うことができません。