如何获得覆盖hashCode()的对象的唯一ID ?

当Java中的类不覆盖hashCode ()时, 打印这个类的实例会得到一个很好的唯一的数字

Object的Javadoc对hashCode ()说:

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

但是当类覆盖hashCode ()时,我怎么能得到 它唯一的数字?< / p >
261552 次浏览

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

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

javadoc for Object指定了这一点

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

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

你可以使用System.identityHashCode来获取任何类的id。

只是从另一个角度扩大其他答案。

如果你想重用来自“上面”的hashcode,并使用类的“不可变状态”派生新的hashcode,那么调用super就可以了。虽然这可能会/可能不会一直级联到Object(即某些祖先可能不会调用super),但它将允许您通过重用派生哈希码。

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

hashCode()和identityHashCode()返回值是不同的。对于两个不相等(用==测试)的对象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");
}
}
}

也许这个快速,肮脏的解决方案会起作用?

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


public int hashCode() {
return uid;
}
}

这也给出了被初始化的类的实例数。

hashCode()方法不是用来为对象提供唯一标识符的。它将对象的状态(即成员字段的值)分解为一个整数。这个值主要用于一些基于散列的数据结构,如map和set,以有效地存储和检索对象。

如果你需要对象的标识符,我建议你添加自己的方法,而不是重写hashCode。为此,您可以创建如下所示的基接口(或抽象类)。

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;
}
}

如果它是一个可以修改的类,你可以声明一个类变量static java.util.concurrent.atomic.AtomicInteger nextInstanceId。(你必须以明显的方式给它一个初始值。)然后声明一个实例变量int instanceId = nextInstanceId.getAndIncrement()

我也遇到了同样的问题,到目前为止,我对任何答案都不满意,因为它们都没有保证唯一的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的任何对象将永远不会被垃圾收集。

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

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

我想出了这个解决方案,在我的情况下,我有对象创建在多线程和序列化:

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

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

Integer.toHexString(hashCode())

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

java.util.UUID.randomUUID()

这个答案是线程安全的,将在不同的虚拟机上工作。

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