简体   繁体   English

如何获取覆盖 hashCode() 的 object 的唯一 ID?

[英]How to get the unique ID of an object which overrides hashCode()?

When a class in Java doesn't override hashCode() , printing an instance of this class gives a nice unique number.当 Java 中的 class 不覆盖hashCode()时,打印此 class 的实例会给出一个很好的唯一编号。

The Javadoc of Object says about hashCode() : Object 的 Javadoc 关于hashCode()说:

As much as is reasonably practical, the hashCode method defined by class Object does return distinct integers for distinct objects.在相当实用的情况下,class Object 定义的 hashCode 方法确实会为不同的对象返回不同的整数。

But when the class overrides hashCode() , how can I get its unique number?但是当 class 覆盖hashCode()时,我怎样才能得到它的唯一编号?

System.identityHashCode(yourObject) will give the 'original' hash code of yourObject as an integer. System.identityHashCode(yourObject)将以整数形式给出yourObject的“原始”哈希码。 Uniqueness isn't necessarily guaranteed.不一定保证唯一性。 The Sun JVM implementation will give you a value which is related to the original memory address for this object, but that's an implementation detail and you shouldn't rely on it. Sun JVM 实现将为您提供一个与此对象的原始内存地址相关的值,但这是一个实现细节,您不应依赖它。

EDIT: Answer modified following Tom's comment below re.编辑:根据汤姆在下面的评论修改答案。 memory addresses and moving objects.内存地址和移动对象。

The javadoc for Object specifies that Object 的 javadoc 指定

This is typically implemented by converting the internal address of the object into an integer, but this implementation technique is not required by the JavaTM programming language.这通常是通过将对象的内部地址转换为整数来实现的,但是 JavaTM 编程语言不需要这种实现技术。

If a class overrides hashCode, it means that it wants to generate a specific id, which will (one can hope) have the right behaviour.如果一个类覆盖了 hashCode,这意味着它想要生成一个特定的 id,这将(可以希望)具有正确的行为。

You can use System.identityHashCode to get that id for any class.您可以使用System.identityHashCode来获取任何类的 id。

hashCode() method is not for providing a unique identifier for an object. hashCode()方法不是为对象提供唯一标识符。 It rather digests the object's state (ie values of member fields) to a single integer.而是将对象的状态(即成员字段的值)消化为单个整数。 This value is mostly used by some hash based data structures like maps and sets to effectively store and retrieve objects.该值主要由一些基于哈希的数据结构(如映射和集合)使用,以有效地存储和检索对象。

If you need an identifier for your objects, I recommend you to add your own method instead of overriding hashCode .如果您需要对象的标识符,我建议您添加自己的方法而不是覆盖hashCode For this purpose, you can create a base interface (or an abstract class) like below.为此,您可以创建如下所示的基本接口(或抽象类)。

public interface IdentifiedObject<I> {
    I getId();
}

Example usage:用法示例:

public class User implements IdentifiedObject<Integer> {
    private Integer studentId;

    public User(Integer studentId) {
        this.studentId = studentId;
    }

    @Override
    public Integer getId() {
        return studentId;
    }
}

Maybe this quick, dirty solution will work?也许这个快速而肮脏的解决方案会奏效?

public class A {
    static int UNIQUE_ID = 0;
    int uid = ++UNIQUE_ID;

    public int hashCode() {
        return uid;
    }
}

This also gives the number of instance of a class being initialized.这也给出了正在初始化的类的实例数。

If it's a class that you can modify, you could declare a class variable static java.util.concurrent.atomic.AtomicInteger nextInstanceId .如果它是一个可以修改的类,则可以声明一个类变量static java.util.concurrent.atomic.AtomicInteger nextInstanceId (You'll have to give it an initial value in the obvious way.) Then declare an instance variable int instanceId = nextInstanceId.getAndIncrement() . (你必须以明显的方式给它一个初始值。)然后声明一个实例变量int instanceId = nextInstanceId.getAndIncrement()

I came up with this solution which works in my case where I have objects created on multiple threads and are serializable:我想出了这个解决方案,它适用于我在多个线程上创建对象并且可序列化的情况:

public abstract class ObjBase implements Serializable
    private static final long serialVersionUID = 1L;
    private static final AtomicLong atomicRefId = new AtomicLong();

    // transient field is not serialized
    private transient long refId;

    // default constructor will be called on base class even during deserialization
    public ObjBase() {
       refId = atomicRefId.incrementAndGet()
    }

    public long getRefId() {
        return refId;
    }
}
// looking for that last hex?
org.joda.DateTime@57110da6

If you're looking into the hashcode Java types when you do a .toString() on an object the underlying code is this:如果您在对象上执行.toString()时查看hashcode Java 类型,则底层代码是这样的:

Integer.toHexString(hashCode())

I had the same issue and was not satisfied with any of the answers so far since none of them guaranteed unique IDs.我遇到了同样的问题,并且对到目前为止的任何答案都不满意,因为它们都不能保证唯一的 ID。

I too wanted to print object IDs for debugging purposed.我也想打印对象 ID 以进行调试。 I knew there must be some way to do it, because in the Eclipse debugger, it specifies unique IDs for each object.我知道一定有办法做到这一点,因为在 Eclipse 调试器中,它为每个对象指定了唯一的 ID。

I came up with a solution based on the fact that the "==" operator for objects only returns true if the two objects are actually the same instance.我想出了一个基于以下事实的解决方案:对象的“==”运算符仅在两个对象实际上是同一个实例时才返回 true。

import java.util.HashMap;
import java.util.Map;

/**
 *  Utility for assigning a unique ID to objects and fetching objects given
 *  a specified ID
 */
public class ObjectIDBank {

    /**Singleton instance*/
    private static ObjectIDBank instance;

    /**Counting value to ensure unique incrementing IDs*/
    private long nextId = 1;

    /** Map from ObjectEntry to the objects corresponding ID*/
    private Map<ObjectEntry, Long> ids = new HashMap<ObjectEntry, Long>();

    /** Map from assigned IDs to their corresponding objects */
    private Map<Long, Object> objects = new HashMap<Long, Object>();

    /**Private constructor to ensure it is only instantiated by the singleton pattern*/
    private ObjectIDBank(){}

    /**Fetches the singleton instance of ObjectIDBank */
    public static ObjectIDBank instance() {
        if(instance == null)
            instance = new ObjectIDBank();

        return instance;
    }

    /** Fetches a unique ID for the specified object. If this method is called multiple
     * times with the same object, it is guaranteed to return the same value. It is also guaranteed
     * to never return the same value for different object instances (until we run out of IDs that can
     * be represented by a long of course)
     * @param obj The object instance for which we want to fetch an ID
     * @return Non zero unique ID or 0 if obj == null
     */
    public long getId(Object obj) {

        if(obj == null)
            return 0;

        ObjectEntry objEntry = new ObjectEntry(obj);

        if(!ids.containsKey(objEntry)) {
            ids.put(objEntry, nextId);
            objects.put(nextId++, obj);
        }

        return ids.get(objEntry);
    }

    /**
     * Fetches the object that has been assigned the specified ID, or null if no object is
     * assigned the given id
     * @param id Id of the object
     * @return The corresponding object or null
     */
    public Object getObject(long id) {
        return objects.get(id);
    }


    /**
     * Wrapper around an Object used as the key for the ids map. The wrapper is needed to
     * ensure that the equals method only returns true if the two objects are the same instance
     * and to ensure that the hash code is always the same for the same instance.
     */
    private class ObjectEntry {
        private Object obj;

        /** Instantiates an ObjectEntry wrapper around the specified object*/
        public ObjectEntry(Object obj) {
            this.obj = obj;
        }


        /** Returns true if and only if the objects contained in this wrapper and the other
         * wrapper are the exact same object (same instance, not just equivalent)*/
        @Override
        public boolean equals(Object other) {
            return obj == ((ObjectEntry)other).obj;
        }


        /**
         * Returns the contained object's identityHashCode. Note that identityHashCode values
         * are not guaranteed to be unique from object to object, but the hash code is guaranteed to
         * not change over time for a given instance of an Object.
         */
        @Override
        public int hashCode() {
            return System.identityHashCode(obj);
        }
    }
}

I believe that this should ensure unique IDs throughout the lifetime of the program.我相信这应该确保在程序的整个生命周期中都有唯一的 ID。 Note, however, that you probably don't want to use this in a production application because it maintains references to all of the objects for which you generate IDs.但是请注意,您可能不想在生产应用程序中使用它,因为它维护对您为其生成 ID 的所有对象的引用。 This means that any objects for which you create an ID will never be garbage collected.这意味着您为其创建 ID 的任何对象都不会被垃圾回收。

Since I'm using this for debug purposes, I'm not too concerned with the memory being freed.由于我将其用于调试目的,因此我不太关心释放的内存。

You could modify this to allow clearing Objects or removing individual objects if freeing memory is a concern.如果释放内存是一个问题,您可以修改它以允许清除对象或删除单个对象。

Just to augment the other answers from a different angle.只是为了从不同的角度增加其他答案。

If you want to reuse hashcode(s) from 'above' and derive new ones using your class' immutatable state, then a call to super will work.如果您想重用来自“above”的哈希码并使用您的类的不可变状态派生新的哈希码,则调用 super 将起作用。 While this may/may not cascade all the way up to Object (ie some ancestor may not call super), it will allow you to derive hashcodes by reuse.虽然这可能/可能不会一直级联到 Object(即某些祖先可能不会调用 super),但它允许您通过重用来派生哈希码。

@Override
public int hashCode() {
    int ancestorHash = super.hashCode();
    // now derive new hash from ancestorHash plus immutable instance vars (id fields)
}

There is a difference between hashCode() and identityHashCode() returns. hashCode() 和 identityHashCode() 返回之间存在差异。 It is possible that for two unequal (tested with ==) objects o1, o2 hashCode() can be the same.对于两个不相等(用 == 测试)的对象 o1,o2 hashCode() 可能是相同的。 See the example below how this is true.请参阅下面的示例,这是如何正确的。

class SeeDifferences
{
    public static void main(String[] args)
    {
        String s1 = "stackoverflow";
        String s2 = new String("stackoverflow");
        String s3 = "stackoverflow";
        System.out.println(s1.hashCode());
        System.out.println(s2.hashCode());
        System.out.println(s3.hashCode());
        System.out.println(System.identityHashCode(s1));
        System.out.println(System.identityHashCode(s2));
        System.out.println(System.identityHashCode(s3));
        if (s1 == s2)
        {
            System.out.println("s1 and s2 equal");
        } 
        else
        {
            System.out.println("s1 and s2 not equal");
        }
        if (s1 == s3)
        {
            System.out.println("s1 and s3 equal");
        }
        else
        {
            System.out.println("s1 and s3 not equal");
        }
    }
}

Since Object.hashCode() and System.identityHashCode() do not provide IDs that are guaranteed to be unique, I think the correct answer is to generate a UUID or GUID:由于Object.hashCode()System.identityHashCode()不提供保证唯一的 ID,我认为正确的答案是生成 UUID 或 GUID:

java.util.UUID.randomUUID()

This answer is thread-safe and will work across different virtual machines .这个答案是线程安全的,并且可以跨不同的虚拟机工作。

For example, the Identifiable class could be extended as follows to provide any class with a unique ID:例如, Identifiable class 可以扩展如下,为任何 class 提供唯一 ID:

public abstract class Identifiable {
    public final UUID id = UUID.randomUUID();
}

...

public class Example extends Identifiable {}

...

public static void main(String[] args) {

    Example example1 = new Example();
    Example example2 = new Example();

    example1.id.toString(); // e.g. 8308798d-7cec-427d-b7f8-7be762f3b5c7
    example1.id.equals(example1.id); // true
    example1.id.equals(example2.id); // false
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 如何覆盖在我定义的Java对象中返回唯一哈希码值及其唯一实体ID的哈希码方法? - How to overwrite the hashcode method that returns a unique hashcode value with its unique entity ID in my defined Java object? 如何在Java中获取具有哈希码或其他唯一字符串的对象引用 - How to get the Object Reference having the hashcode or other Unique String in Java 使用哈希码获取唯一ID - Using hashcode for a unique ID 覆盖equals()和hashCode()的POJO是什么? - What is a POJO which overrides equals() and hashCode() called? Java:获取对象的唯一属性(例如哈希码,但具有防碰撞功能) - Java: get a unique property of an object (like hashcode, but collision proof) 如何获取HttpsUrlConnection对象的唯一ID - How to get unique ID of HttpsUrlConnection Object 如何获取Firebase中唯一ID内的数据 - How to get the data which is inside a unique id in firebase 是否可以使用 hashCode 作为内容唯一 ID? - Is it possible to use hashCode as a content unique ID? 如何使ListFragment内部的可点击对象也覆盖onListItemClick - How to make a clickable object inside a ListFragment which also Overrides onListItemClick 如何在 Firebase Android 上的 recyclerView 中获取对象的唯一 ID? - How to get object's unique id within a recyclerView on Firebase Android?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM