[英]Unique id for Java object
我將 java 對象索引到 Elasticsearch 中。 以下是 class 的結構:
public Class Document{
private String name;
private double value;
private Date date;
private Map<String, String> attributes;
//getters and setters
}
在我索引任何 object 之前,我想計算/導出 object 的唯一 ID,它應該基於這些成員的值。 如果我構造另一個 object 具有相同的名稱、日期、值和屬性值(即,如果鍵值對的數量和值相同),那么 ID 也應該相同。
目前,我正在使用Objects.hash(Object... objects)
來計算 hashCode 並將該 hashCode 設置為 id。 它似乎工作正常。 對於這些屬性具有相同值的對象,它返回相同的 integer。 但是,考慮到 java 中 int 的文檔數量和范圍,哈希碼可能/可能不同(這將導致重復文檔)。
對此有任何替代解決方案嗎? 我們可以根據這些值創建一個字母數字字符串(或其他東西)嗎?
提前致謝。
除非您將對象本身用作鍵,否則您將無法完全避免沖突……如果您希望這樣做,則可以將值序列化為一個字節序列,即8個字節表示date
double
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);
您應該重寫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
}
最終有這樣的事情:
/**
* 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,這對我來說是更好的方法:) 例如, int哈希可以具有40億個值,因此,一個值的概率為1 /(40億),並且在其他地方具有相同編號的數字是1 / 4b x 1 / 4b x 1 / 4b x 1 / 4b,即(1 / 4b)^ 4(如果我沒記錯的話)。
不知道這是否是最合適的方法。 但這似乎奏效了。
謝謝
hashCode() 給出 32 位,如果這將有沖突的風險,請使用不同的散列算法。
java.security.MessageDigest 在 Java 中提供選項
我會為此推薦“MD5”,它會為您提供 128 位數字
"MD5" = 128 bits
"SHA1" = 160 bits
"SHA-256" = 256 bits
"SHA-384" = 384 bits
"SHA-512" = 512 bits
您不必擔心 md5 或 sha-1 的加密問題
權衡 hash 的大小,有碰撞的機會。
總是有碰撞的危險,要完全避免它把貓元素串在一起。 以 16,32 或 64 為基數表示數字以節省一點空間。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.