2015-02-05 95 views
0

我正在將java對象編入Elasticsearch。以下是一類的結構:Java對象的唯一標識

public Class Document{ 
    private String name; 
    private double value; 
    private Date date; 
    private Map<String, String> attributes; 
    //getters and setters 
} 

I指數的任何對象之前,我想計算/得出的唯一ID時應該根據這些成員的值的對象。如果我爲名稱,日期,值和屬性構造另一個具有相同值的對象(即,如果鍵值對的數量和值相同),則ID也應該相同。

目前,我使用Objects.hash(Object... objects)來計算hashCode並將該hashCode設置爲id。它似乎工作正常。它爲這些屬性具有相同值的對象返回相同的整數。但是,考慮到java中int數量的文檔和範圍,hashcode可能會/可能不會相同(這會導致重複的文檔)。

任何替代解決方案呢?我們可以根據這些值創建一個字母數字字符串(或其他)嗎?

在此先感謝。

+1

是否有任何對象的元素是唯一的?你需要爲這些對象中的每一個提供某種類型的ID。 – Qwertyzw 2015-02-05 23:15:32

+0

沒有一個元素是唯一的。但是,所有元素的組合應該是唯一的。 – 2015-02-05 23:17:03

+0

你的意思是你擔心所有哈希碼的大小是否足夠大量的對象? – MageXellos 2015-02-05 23:20:41

回答

0

你不會是完全能夠避免衝突,除非您使用對象本身作爲一個重要...如果你想這樣做,你可以連載您的值轉換爲一個字節序列,即8個字節(因爲內部表示long,並根據您的name的字節長度任意數量的...

最明智的做法就是使用這些值來計算的hashCode,然後當碰撞發生時會逐個比較每個成員以確保相等。這就是java Hashtable的工作方式。

如果你想繼續前進,創建你 「絕對是獨一無二的標識符」 雖然...

byte[] defoUnique = new byte[24 + name.size()]; 
byte[] dateBytes = Long.toByteArray(date.getTime()); 
for (int i = 0 ; i < 8 ; i++) defoUnique[i] = dateBytes[i]; 
byte[] valueBytes = Long.toByteArray(Double.doubleToLongBits(value)); 
for (int i = 0 ; i < 8 ; i++) defoUnique[i+8] = valueBytes[i]; 
byte[] nameBytes = name.getBytes(); 
for (int i = 0 ; i < nameBytes.length ; i++) defoUnique[i+16] = nameBytes[i]; 

/* Make byte sequence into alphanumeric string */ 
String identifierString = Base64.getEncoder().encodeToString(defoUnique); 
+0

我不知道如何使用對象本身作爲一個關鍵..我想要的是一個數字(或字母數字)的id,這將是'有意義'唯一的一個對象。即如果存在具有相同值的另一個對象,則該id將是相同的。 但是,在插入對象時,我將無法訪問存儲在ES中的現有數據(對象)。所以,我會生成一個id。如果有另一個具有相同值的對象,我的id將匹配它的id,因此,我的對象不會被插入。這就是我要的。 謝謝。 – 2015-02-05 23:51:06

+0

好的,在這種情況下,您可以將我在這裏提供的'byte []'數組使用base64進行編碼:http://docs.oracle.com/javase/8/docs/api/java/util/ Base64.Encoder.html#encodeToString字節:A- – robert 2015-02-05 23:55:07

0

你應該重寫equals()和hashCode()。 (不一起覆蓋它們是常見的錯誤)。

下面是一個例子。這些想法是創造平等的每個對象和測試的哈希碼(不管你得到你的對象返回與否)

例:

// from http://commons.apache.org/proper/commons-lang/apidocs/org/apache/commons/lang3/builder/HashCodeBuilder.html 
    public class Person { 
     String name; 
     int age; 
     boolean smoker; 
     int id; // this is your bit 

     public int hashCode() { 
     // you pick a hard-coded, randomly chosen, non-zero, odd number 
     // ideally different for each class 
     return new HashCodeBuilder(17, 37). 
      append(name). 
      append(age). 
      append(smoker). 
      toHashCode(); 
     } 
    } 

    public boolean equals(Object obj) { 
    // the next 3 ifs are a 'short' circuit' 
     if (obj == null) { return false; } 
     if (obj == this) { return true; } 
     if (obj.getClass() != getClass()) { 
     return false; 
     } 

     // the meat of it 
     MyClass rhs = (MyClass) obj; 

     boolean sameClass = new EqualsBuilder() 
        .appendSuper(super.equals(obj)) 
        .append(field1, rhs.field1) 
        .append(field2, rhs.field2) 
        .append(field3, rhs.field3) 
        .isEquals(); 

     // here set/update your id 
      if (sameClass){ 
       this.id = rhs.id 
      } 

      return sameClass 
      } 
0

結束有這樣的事情:

/** 
    * Sets the id of document by calculating hash for individual elements 
    */ 
    public void calculateHash(){ 
     ByteBuffer byteBuffer = ByteBuffer.allocate(16); 
     byteBuffer.putInt(Objects.hashCode(name)); 
     byteBuffer.putInt(Objects.hashCode(date)); 
     byteBuffer.putInt(Objects.hashCode(value)); 
     byteBuffer.putInt(Objects.hashCode(attributes)); 
     super.setId(DigestUtils.sha512Hex(byteBuffer.array())); 
     byteBuffer.clear(); 
    } 

所以,基本上,我計算單個元素的哈希值,將它們填充到一個字節數組中,然後計算它的SHA-1哈希值。所以碰撞的機率非常小。即使是一個哈希碰撞,其他哈希也不太可能相互衝突(因爲它是4個哈希的組合)。我認爲碰撞的可能性是(1/4十億)^ 4,這對我來說更爲有利:) E.g. int hash可以有40億個值,因此,一個值的概率是1 /(40億),其他地方具有相同數字的概率是1/4b x 1/4b x 1/4b x 1/4b ie (1/4b)^ 4如果我沒有錯。

不知道它是否是最合適的(或合適的)方式。但它似乎奏效了。

謝謝