2013-01-14 53 views
9

我有一個用Java編寫的JSON模式實現,它取決於Jackson(版本2.1.x)。出於準確性原因,我告訴傑克遜使用BigDecimal作爲浮點數。「正常化」BigDecimal的哈希碼:howto?

對於JSON模式的需要,有一個特殊的需求:對於數值,JSON值的相等性是由它們的數學值相等來定義的。我需要這種檢查,因爲,舉例來說,這不是一個法律架構(在enum值應該是唯一的):

{ "enum": [ 1, 1.0 ] } 

但JsonNodes爲11.0是不相等的。因此,我編寫了番石榴的​​的實施方案,並在適當的地方使用Set<Equivalence.Wrapper<JsonNode>>。這個實現應該適用於所有類型的節點,而不僅僅是數字節點。

實施這一項目的最困難的部分原來是doHash()爲數字節點:/我需要等效數學值相同的hashCode,無論是整數或浮點數。

我能想出目前最好的是這樣的:

@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的完整的代碼)

+0

@zsxwing:doEquivalent已經被覆蓋 - 請參閱編輯,我已經添加了一個鏈接到完整的實現 – fge

+2

不清楚 - 是否有問題,代碼沒有返回相等的哈希碼相等的值,或者你錯誤地)試圖確保每一個獨特的值獨特的哈希碼? –

+0

你想讓「1」,「1.0」,「1.00」返回相同的哈希碼嗎?也許你可以使用不使用hashCode的TreeSet? – zsxwing

回答

12

則轉換爲DOUBLE,並使用雙的的hashCode,但基地平等的BigDecimal的compareTo順序。

兩個數值上等效的BigDecimals將映射到相同的Double,並獲得相同的hashCode。一些稍微不同的BigDecimal值會由於雙舍入而獲得相同的哈希碼,但是大多數不同的值將得到不同的哈希碼,這就是您所需要的。

+1

我確實使用'.compareTo()'來實現平等。這是一個非常簡單的解決方案,我沒有想過... – fge

+0

我很好奇,但是,對於非常大的值返回的double值是什麼,哪個double由於缺少精度而無法處理? – fge

+1

所有大於Double.MAX_VALUE的數字都將映射到無窮大,並獲得相同的哈希碼。同樣,非常小的數字將映射到零,並獲得相同的哈希碼。否則,匹配約16個最有效數字的不同數字對將得到相同的哈希碼。 –