[英]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.