如何获取覆盖hashCode()的对象的唯一标识符?
当Java中的一个类没有重写hashCode()时 ,打印这个类的实例会给出一个很好的唯一编号。
Object的Javadoc说关于hashCode() :
尽可能多地合理实用,由类Object定义的hashCode方法确实为不同的对象返回不同的整数。
但是当类重写hashCode()时 ,我怎样才能得到它的唯一编号?
System.identityHashCode(yourObject)将把yourObject的“原始”散列码作为一个整数。 唯一性不一定得到保证。 Sun JVM实现将为您提供一个与此对象的原始内存地址相关的值,但这是一个实现细节,您不应该依赖它。
编辑:回答下面的汤姆的评论修改修改。 内存地址和移动对象。
Object的javadoc指定
这通常通过将对象的内部地址转换为整数来实现,但JavaTM编程语言不需要此实现技术。
如果一个类重写hashCode,这意味着它想要生成一个特定的ID,这将(可以希望)有正确的行为。
你可以使用System.identityHashCode来获取任何类的id。
也许这个快速,肮脏的解决scheme将工作
public class A { static int UNIQUE_ID = 0; int uid = ++UNIQUE_ID; public int hashCode() { return uid; } }
这也给出了正在初始化的类的实例的数量。
hashCode()
方法不是为对象提供唯一的标识符。 它将对象的状态(即成员字段的值)消解为一个整数。 这个值主要被一些基于哈希的数据结构(比如地图和集合)用来有效地存储和检索对象。
如果你需要一个标识符为你的对象,我build议你添加你自己的方法,而不是重写hashCode
。 为此,您可以创build一个基本接口(或抽象类),如下所示。
public interface IdentifiedObject<I> { I getId(); }
用法示例:
public class User implements IdentifiedObject<Integer> { private Integer studentId; public User(Integer studentId) { this.studentId = studentId; } @Override public Integer getId() { return studentId; } }
对于id代,你可以检查我的博客文章 ,我试图解释一些方法来产生独特的id。
如果它是一个你可以修改的类,你可以声明一个类variablesstatic java.util.concurrent.atomic.AtomicInteger nextInstanceId
。 (你将不得不以明显的方式给它一个初始值。)然后声明一个实例variablesint instanceId = nextInstanceId.getAndIncrement()
。
只是为了从另一个angular度增加其他答案。
如果你想重用“上面的”哈希码,并使用你的类的不可变状态派生新的哈希码,那么超级的调用将起作用。 虽然这可能/不可能一直级联到对象(即某些祖先可能不会调用超级),但它将允许您通过重用来获取哈希码。
@Override public int hashCode() { int ancestorHash = super.hashCode(); // now derive new hash from ancestorHash plus immutable instance vars (id fields) }
hashCode()和identityHashCode()返回之间有区别。 对于两个不相等的(用==testing的)对象o1,o2 hashCode()可能是相同的。 看下面的例子是如何的这是真实的。
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"); } } }
我有同样的问题,并不满意任何答案,因为他们都没有保证唯一的ID。
我也想打印对象ID用于debugging目的。 我知道必须有一些方法来完成它,因为在Eclipsedebugging器中,它为每个对象指定了唯一的ID。
我想出了一个解决scheme,基于这样一个事实,如果两个对象实际上是相同的实例,对象的“==”运算符只返回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的对象的引用。 这意味着您创buildID的任何对象将永远不会被垃圾收集。
由于我用于debugging目的,我不太关心内存被释放。
如果释放内存是一个问题,您可以修改它以允许清除对象或删除单个对象。
我想出了这个解决scheme,在我的情况下,我有多个线程创build对象,并可序列化:
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; } }