2013-01-14 11 views
9

私はJackson(バージョン2.1.x)に依存するJavaで書かれたJSONスキーマの実装を持っています。正確な理由から、私はJacksonに浮動小数点数にBigDecimalを使用するよう指示します。"正規化" BigDecimalのハッシュコード:howto?

JSONスキーマのニーズには、特に必要なものがあります。数値のJSON値の等価は、数学的価値の等価性によって定義されます。例えば、これは法律上のスキーマ(enumの値は一意である必要があります)ではない、ので、私はチェックのこの種のものが必要です。

{ "enum": [ 1, 1.0 ] } 

しかし11.0ためJsonNodesは等しくありません。したがって、私はGuavaの​​の実装をコード化し、必要に応じてSet<Equivalence.Wrapper<JsonNode>>を使用しました。また、この実装は、数値ノードだけでなく、すべてのタイプのノードで機能するはずです。

そして、この実装の最も難しい部分は、数値ノードに対するdoHash()であることが判明:/私は、彼らが整数または浮動小数点数であるかどうか同等の数学値、用同じハッシュコードを必要とします。

私は、現時点では思い付くことができる最高はこれです:

@Override 
protected int doHash(final JsonNode t) 
{ 
    /* 
    * If this is a numeric node, we want a unique hashcode for all possible 
    * number nodes. 
    */ 
    if (t.isNumber()) { 
     final BigDecimal decimal = t.decimalValue(); 
     try { 
      return decimal.toBigIntegerExact().hashCode(); 
     } catch (ArithmeticException ignored) { 
      return decimal.stripTrailingZeros().hashCode(); 
     } 
    } 

    // etc etc -- the rest works fine 

これは、現時点では、私が思い付くことが最高です。

このようなハッシュコードを計算するには、より良い方法がありますか?

編集:等価実装hereの完全なコード)のBigDecimalのcompareTo順をダブルとダブルのハッシュコードを使用する

+0

@zsxwing:doEquivalentはすでにオーバーライドされています。編集を参照してください。 – fge

+2

完全な実装へのリンクを追加しました。明確ではありません - コードが等価のハッシュコードを返していないか、誤って別の値ごとにユニークなハッシュコードを保証しようとしていますか? –

+0

"1"、 "1.0"、 "1.00"は同じハッシュコードを返しますか?多分、あなたはhashCodeを使わないTreeSetを使うことができますか? – zsxwing

答えて

12

変換が、ベースの平等。

2つの数値的に等しいBigDecimalは、同じDoubleにマップされ、同じhashCodeを取得します。非常にわずかに異なるいくつかのBigDecimal値は、二重丸めのために同じハッシュコードを取得しますが、ほとんどの異なる値は異なるハッシュコードを取得します。

+1

私は '.compareTo()'を実際に使用します。それは私がそれについて考えなかったとてもシンプルな解決策です... – fge

+0

非常に大きな値に対してどのようなdouble値が返されるのか不思議ですが、 'double'は精度の欠如のために処理できません。 – fge

+1

Double.MAX_VALUEより大きい数値はすべて無限大にマップされ、同じハッシュコードを取得します。同様に、非常に小さい数値はゼロにマップされ、同じハッシュコードを取得します。それ以外の場合、16桁の上位桁に一致する別々の数字の対は、同じハッシュコードを取得します。 –