2013-01-14 17 views
9

Ho un'implementazione JSON Schema scritta in Java che dipende da Jackson (versione 2.1.x). Per ragioni di precisione, dico a Jackson di usare BigDecimal per i numeri in virgola mobile."Normalizzazione" del codice hash di BigDecimal: howto?

Per le esigenze dello schema JSON, vi è una particolare esigenza: l'uguaglianza del valore JSON, per i valori numerici, è definita dall'uguaglianza del loro valore matematico. Ho bisogno di questo tipo di controllo dal momento che, per esempio, non si tratta di uno schema legale (valori in un enum dovrebbe essere unico):

{ "enum": [ 1, 1.0 ] } 

Ma JsonNodes per 1 e 1.0 non sono uguali. Pertanto, ho codificato un'implementazione di Guava Equivalence, e utilizzare Set<Equivalence.Wrapper<JsonNode>> dove appropriato. E questa implementazione dovrebbe funzionare per tutti i tipi di nodi, non solo per i nodi numerici.

E la parte più difficile di questa implementazione risulta essere doHash() per nodi numerici:/Ho bisogno lo stesso codice hash per valori matematici equivalenti, che siano numeri interi o numeri in virgola mobile.

il meglio che potevo venire con al momento è questa:

@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 

Questo è, al momento, il meglio che ho potuto trovare.

Esiste un modo migliore per calcolare un codice hash di questo tipo?

(edit: in pieno il codice della realizzazione Equivalenza here)

+0

@zsxwing: doEquivalent è già scaduto - vedi modifica, ho aggiunto un collegamento all'implementazione completa – fge

+2

Non chiaro - c'è un problema che il codice non restituisce uguali codici hash per valori uguali, oppure sei tu (erroneamente) cercando di assicurare un codice hash univoco per ogni valore distinto? –

+0

Vuoi che "1", "1.0", "1.00" restituiscano lo stesso codice hash? Forse puoi usare TreeSet che non usa hashCode? – zsxwing

risposta

12

Converti in raddoppiare e utilizzare hashCode del doppio, ma l'uguaglianza di base sull'ordine BigDecimal compareTo.

Due BigDecimali numericamente equivalenti si assoceranno allo stesso doppio e otterranno lo stesso codice hash. Alcuni valori BigDecimal leggermente differenti avranno lo stesso codice hash a causa del doppio arrotondamento, ma la maggior parte dei valori distinti otterrà hash diversi, che è tutto ciò che serve.

+1

Io uso '.compareTo()' per l'uguaglianza. Questa è una soluzione così semplice che non ci ho pensato ... – fge

+0

Sono curioso, però, riguardo a quali valori double vengono restituiti per valori molto grandi, che 'double' non può gestire a causa di una mancanza di precisione? – fge

+1

Tutti i numeri maggiori di Double.MAX_VALUE verranno mappati su infinito e otterranno lo stesso codice hash. Allo stesso modo, numeri molto piccoli verranno mappati a zero e otterranno lo stesso codice hash. Altrimenti, coppie di numeri distinti corrispondenti alle 16 cifre più significative otterranno lo stesso codice hash. –