繁体   English   中英

Java object 的唯一 ID

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

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM