当Java中的类不重写hashCode()时, 打印这个类的实例会得到一个很好的唯一编号。

Object的Javadoc说hashCode():

在合理实用的情况下,类Object定义的hashCode方法确实为不同的对象返回不同的整数。

但是当类重写hashCode()时,我如何获得 它唯一的数字?


当前回答

由于Object.hashCode()和System.identityHashCode()不提供保证唯一的id,我认为正确的答案是生成一个UUID或GUID:

java.util.UUID.randomUUID()

这个答案是线程安全的,并且可以跨不同的虚拟机工作。

例如,可识别类可以被扩展为提供任何具有唯一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
}

其他回答

我也遇到了同样的问题,到目前为止,我对任何答案都不满意,因为它们都没有保证唯一的id。

我也想打印用于调试的对象id。我知道一定有某种方法可以做到这一点,因为在Eclipse调试器中,它为每个对象指定了唯一的id。

我提出了一个解决方案,即对象的“==”操作符仅在两个对象实际上是同一个实例时才返回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);
        }
    }
}

我认为这应该确保在程序的整个生命周期中都有唯一的id。但是请注意,您可能不希望在生产应用程序中使用它,因为它维护对您生成id的所有对象的引用。这意味着您为其创建ID的任何对象将永远不会被垃圾收集。

由于我将此用于调试目的,所以我不太关心释放的内存。

如果需要考虑释放内存,可以修改此选项以允许清除对象或删除单个对象。

由于Object.hashCode()和System.identityHashCode()不提供保证唯一的id,我认为正确的答案是生成一个UUID或GUID:

java.util.UUID.randomUUID()

这个答案是线程安全的,并且可以跨不同的虚拟机工作。

例如,可识别类可以被扩展为提供任何具有唯一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
}

System.identityHashCode(yourObject)将给出yourObject的“原始”哈希码作为整数。独特性不一定得到保证。Sun JVM实现将为您提供一个与该对象的原始内存地址相关的值,但这是一个实现细节,您不应该依赖它。

编辑:回答修改以下汤姆的评论re.内存地址和移动对象。

javadoc for Object指定了这一点

这通常是通过将对象的内部地址转换为整数来实现的,但是JavaTM编程语言并不需要这种实现技术。

如果一个类重写了hashCode,这意味着它想要生成一个特定的id,它将(人们可以希望)具有正确的行为。

你可以使用系统。identityHashCode来获取任何类的id。

// looking for that last hex?
org.joda.DateTime@57110da6

如果你在对象上执行.toString()时查看hashcode Java类型,底层代码是这样的:

Integer.toHexString(hashCode())