When a class in Java doesn't override hashCode(), printing an instance of this class gives a nice unique number.
当Java中的一个类没有覆盖hashCode()时,打印这个类的一个实例会得到一个很好的惟一数字。
The Javadoc of Object says about hashCode():
对象的Javadoc表示hashCode():
As much as is reasonably practical, the hashCode method defined by class Object does return distinct integers for distinct objects.
类对象定义的hashCode方法为不同的对象返回不同的整数。
But when the class overrides hashCode(), how can I get it's unique number?
但是当类覆盖hashCode()时,我如何得到它的唯一编号呢?
9 个解决方案
#1
285
System.identityHashCode(yourObject) will give the 'original' hash code of yourObject as an integer. 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.
System.identityHashCode(yourObject)会将您的“原始”哈希代码作为一个整数。独特性并不一定保证。Sun JVM实现将为您提供与该对象的原始内存地址相关的值,但这是实现细节,您不应该依赖它。
EDIT: Answer modified following Tom's comment below re. memory addresses and moving objects.
编辑:根据Tom的评论修改的答案。内存地址和移动对象。
#2
26
The javadoc for Object specifies that
对象的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,这个id将(我们可以希望)具有正确的行为。
You can use System.identityHashCode to get that id for any class.
您可以使用系统。为任何类获取该id的标识hashcode。
#3
7
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.
这还提供了正在初始化的类的实例数。
#4
7
hashCode()
method is not for providing a unique identifier for an object. It rather digests the object's state (i.e. 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.
hashCode()方法不用于为对象提供惟一标识符。它将对象的状态(即成员字段的值)分解为单个整数。这个值主要用于一些基于散列的数据结构,如映射和设置,以有效地存储和检索对象。
If you need an identifier for your objects, I recommend you to add your own method instead of overriding hashCode
. For this purpose, you can create a base interface (or an abstract class) like below.
如果您需要对象的标识符,我建议您添加自己的方法,而不是覆盖hashCode。为此,您可以创建一个基本接口(或抽象类),如下所示。
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;
}
}
For id generation you can check my blog post that I tried to explain some ways to generate unique ids.
对于id生成,您可以查看我的博客文章,我试图解释生成唯一id的一些方法。
#5
3
If it's a class that you can modify, you could declare a class variable 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()
.
如果可以修改类,可以声明类变量静态java.util.concurrent.atomic。AtomicInteger nextInstanceId。(你必须以显而易见的方式给它一个初始值。)然后声明一个实例变量int instanceId = nextInstanceId.getAndIncrement()。
#6
0
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. While this may/may not cascade all the way up to Object (i.e. some ancestor may not call super), it will allow you to derive hashcodes by reuse.
如果您希望重用“上面”的hashcode,并使用您的类“不可更改状态”派生新代码,那么调用super将会起作用。虽然这可能/可能不会一直到对象(比如某些祖先可能不会调用super),但它将允许您通过重用派生出hashcode。
@Override
public int hashCode() {
int ancestorHash = super.hashCode();
// now derive new hash from ancestorHash plus immutable instance vars (id fields)
}
#7
0
There is a difference between hashCode() and identityHashCode() returns. It is possible that for two unequal (tested with ==) objects o1, o2 hashCode() can be the same. See the example below how this is true.
hashCode()和identityHashCode()返回之间存在差异。对于两个不相等(用==测试)的对象o1, o2 hashCode()可以是相同的。请看下面的例子,这是怎么回事。
class SeeDifferences
{
public static void main(String[] args)
{
String s1 = "*";
String s2 = new String("*");
String s3 = "*";
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");
}
}
}
#8
0
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. I knew there must be some way to do it, because in the Eclipse debugger, it specifies unique IDs for each object.
我也想打印用于调试的对象id。我知道一定有某种方法可以做到这一点,因为在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. 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. This means that any objects for which you create an ID will never be garbage collected.
我认为这应该确保在程序的整个生命周期中都有惟一的id。但是,请注意,您可能不希望在生产应用程序中使用它,因为它维护对生成IDs的所有对象的引用。这意味着创建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.
如果需要释放内存,您可以修改它以允许清除对象或删除单个对象。
#9
0
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;
}
}
#1
285
System.identityHashCode(yourObject) will give the 'original' hash code of yourObject as an integer. 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.
System.identityHashCode(yourObject)会将您的“原始”哈希代码作为一个整数。独特性并不一定保证。Sun JVM实现将为您提供与该对象的原始内存地址相关的值,但这是实现细节,您不应该依赖它。
EDIT: Answer modified following Tom's comment below re. memory addresses and moving objects.
编辑:根据Tom的评论修改的答案。内存地址和移动对象。
#2
26
The javadoc for Object specifies that
对象的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,这个id将(我们可以希望)具有正确的行为。
You can use System.identityHashCode to get that id for any class.
您可以使用系统。为任何类获取该id的标识hashcode。
#3
7
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.
这还提供了正在初始化的类的实例数。
#4
7
hashCode()
method is not for providing a unique identifier for an object. It rather digests the object's state (i.e. 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.
hashCode()方法不用于为对象提供惟一标识符。它将对象的状态(即成员字段的值)分解为单个整数。这个值主要用于一些基于散列的数据结构,如映射和设置,以有效地存储和检索对象。
If you need an identifier for your objects, I recommend you to add your own method instead of overriding hashCode
. For this purpose, you can create a base interface (or an abstract class) like below.
如果您需要对象的标识符,我建议您添加自己的方法,而不是覆盖hashCode。为此,您可以创建一个基本接口(或抽象类),如下所示。
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;
}
}
For id generation you can check my blog post that I tried to explain some ways to generate unique ids.
对于id生成,您可以查看我的博客文章,我试图解释生成唯一id的一些方法。
#5
3
If it's a class that you can modify, you could declare a class variable 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()
.
如果可以修改类,可以声明类变量静态java.util.concurrent.atomic。AtomicInteger nextInstanceId。(你必须以显而易见的方式给它一个初始值。)然后声明一个实例变量int instanceId = nextInstanceId.getAndIncrement()。
#6
0
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. While this may/may not cascade all the way up to Object (i.e. some ancestor may not call super), it will allow you to derive hashcodes by reuse.
如果您希望重用“上面”的hashcode,并使用您的类“不可更改状态”派生新代码,那么调用super将会起作用。虽然这可能/可能不会一直到对象(比如某些祖先可能不会调用super),但它将允许您通过重用派生出hashcode。
@Override
public int hashCode() {
int ancestorHash = super.hashCode();
// now derive new hash from ancestorHash plus immutable instance vars (id fields)
}
#7
0
There is a difference between hashCode() and identityHashCode() returns. It is possible that for two unequal (tested with ==) objects o1, o2 hashCode() can be the same. See the example below how this is true.
hashCode()和identityHashCode()返回之间存在差异。对于两个不相等(用==测试)的对象o1, o2 hashCode()可以是相同的。请看下面的例子,这是怎么回事。
class SeeDifferences
{
public static void main(String[] args)
{
String s1 = "*";
String s2 = new String("*");
String s3 = "*";
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");
}
}
}
#8
0
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. I knew there must be some way to do it, because in the Eclipse debugger, it specifies unique IDs for each object.
我也想打印用于调试的对象id。我知道一定有某种方法可以做到这一点,因为在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. 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. This means that any objects for which you create an ID will never be garbage collected.
我认为这应该确保在程序的整个生命周期中都有惟一的id。但是,请注意,您可能不希望在生产应用程序中使用它,因为它维护对生成IDs的所有对象的引用。这意味着创建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.
如果需要释放内存,您可以修改它以允许清除对象或删除单个对象。
#9
0
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;
}
}