如果Java中的类未覆盖hashCode(),则打印该类的实例将提供一个不错的唯一数字。
Object的Javadoc谈到了hashCode():
在合理可行的范围内,由Object类定义的hashCode方法确实为不同的对象返回不同的整数。
但是,当类重写hashCode()时,如何获得其唯一编号?
如果Java中的类未覆盖hashCode(),则打印该类的实例将提供一个不错的唯一数字。
Object的Javadoc谈到了hashCode():
在合理可行的范围内,由Object类定义的hashCode方法确实为不同的对象返回不同的整数。
但是,当类重写hashCode()时,如何获得其唯一编号?
Answers:
System.identityHashCode(yourObject)将给出yourObject的“原始”哈希码为整数。唯一性不一定得到保证。Sun JVM实现将为您提供一个与此对象的原始内存地址相关的值,但这是实现的详细信息,您不应依赖它。
编辑:答案下面汤姆的评论重新修改。内存地址和移动对象。
System.identityHashCode()
?
对象的javadoc指定
通常通过将对象的内部地址转换为整数来实现,但是JavaTM编程语言不需要此实现技术。
如果一个类覆盖了hashCode,则意味着它想要生成一个特定的ID,该ID将(具有希望的)行为正确。
您可以使用System.identityHashCode来获取任何类的ID。
hashCode()
方法不是为对象提供唯一标识符。而是将对象的状态(即成员字段的值)摘要为一个整数。此值主要由某些基于哈希的数据结构(如地图和集合)使用,以有效存储和检索对象。
如果您需要对象的标识符,建议您添加自己的方法,而不要覆盖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;
}
}
也许这种快速,肮脏的解决方案会起作用?
public class A {
static int UNIQUE_ID = 0;
int uid = ++UNIQUE_ID;
public int hashCode() {
return uid;
}
}
这也给出了正在初始化的类的实例数。
System.identityHashCode
是一个更好的解决方案
AtomicLong
在此答案中使用。
我想出了这个解决方案,该解决方案适用于在多个线程上创建并可序列化的对象的情况:
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;
}
}
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");
}
}
}
我遇到了同样的问题,到目前为止,对任何答案都不满意,因为它们都不能保证唯一的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的任何对象都不会被垃圾回收。
因为我将其用于调试目的,所以我不太担心释放内存。
如果需要释放内存,则可以修改此选项以允许清除对象或删除单个对象。