2013-01-14 15 views
9

Mam implementację schematu JSON napisaną w Javie, która zależy od Jackson (wersja 2.1.x). Ze względu na dokładność mówię Jacksonowi, aby używał BigDecimal dla liczb zmiennoprzecinkowych."Normalizowanie" Kod skrótu BigDecimal: howto?

Dla potrzeb schematu JSON istnieje szczególne zapotrzebowanie: Równość wartości JSON dla wartości numerycznych jest definiowana przez równość ich wartości matematycznej. Potrzebuję tego rodzaju czeku od, na przykład, nie jest to schemat prawny (wartości w enum powinny być unikalne):

{ "enum": [ 1, 1.0 ] } 

Ale JsonNodes dla 1 i 1.0 nie są równe. Dlatego też zakodowałem implementację Guva o numerze Equivalence i w razie potrzeby używam Set<Equivalence.Wrapper<JsonNode>>. A ta implementacja powinna działać dla wszystkich typów węzłów, a nie tylko dla węzłów numerycznych.

I najtrudniejsza część tej realizacji okazuje się doHash() dla węzłów numerycznych:/Muszę ten sam hashcode dla równoważnych wartościach matematycznych, czy są liczbami całkowitymi lub liczb zmiennoprzecinkowych.

Najlepszym mogę wymyślić w tej chwili jest tak:

@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 

Jest to w tej chwili najlepsze co mogłem wymyślić.

Czy istnieje lepszy sposób obliczania takiego kodu skrótu?

(edit: pełny kod realizacji Równoważność here)

+0

@zsxwing: doEquivalent jest już overriden - patrz edit, dodałem link do pełnej implementacji – fge

+2

Not clear - czy istnieje problem, że kod nie zwraca kodów równych dla równych wartości, czy też (omyłkowo) próbując zapewnić unikalny kod skrótu dla każdej odrębnej wartości? –

+0

Czy chcesz, aby "1", "1.0", "1,00" zwracały ten sam kod skrótu? Może możesz użyć TreeSet, który nie używa hashCode? – zsxwing

Odpowiedz

12

Konwersja do podwójnego i wykorzystać hashCode podwójnej, lecz równość bazowej na celu BigDecimal CompareTo.

Dwa odpowiedniki liczbowe BigDecimals będą mapować na to samo Double i uzyskać ten sam kod hash. Niektóre wartości BigDecimal, które są bardzo nieznacznie różne, będą miały ten sam kod skrótu z powodu podwójnego zaokrąglania, ale większość wyraźnych wartości otrzyma różne kody hash, które są wszystkim, czego potrzebujesz.

+1

Używam '.compareTo()' do równości. To jest takie proste rozwiązanie, o którym nie myślałem ... – fge

+0

Jestem ciekawy, jakie wartości zwracane są dla bardzo dużych wartości, których "podwójne" nie może obsłużyć z powodu braku precyzji? – fge

+1

Wszystkie liczby większe niż Double.MAX_VALUE zostaną zmapowane do nieskończoności i uzyskają ten sam kod skrótu. Podobnie, bardzo małe liczby zostaną odwzorowane do zera i uzyskają ten sam kod skrótu. W przeciwnym razie pary różnych liczb, które pasują do około 16 najbardziej znaczących cyfr, uzyskają ten sam kod skrótu. –