![](/img/trans.png)
[英]How to overwrite the hashcode method that returns a unique hashcode value with its unique entity ID in my defined Java object?
[英]How to get the unique ID of an object which overrides hashCode()?
當 Java 中的 class 不覆蓋hashCode()時,打印此 class 的實例會給出一個很好的唯一編號。
Object 的 Javadoc 關於hashCode()說:
在相當實用的情況下,class Object 定義的 hashCode 方法確實會為不同的對象返回不同的整數。
但是當 class 覆蓋hashCode()時,我怎樣才能得到它的唯一編號?
System.identityHashCode(yourObject)將以整數形式給出yourObject的“原始”哈希碼。 不一定保證唯一性。 Sun JVM 實現將為您提供一個與此對象的原始內存地址相關的值,但這是一個實現細節,您不應依賴它。
編輯:根據湯姆在下面的評論修改答案。 內存地址和移動對象。
Object 的 javadoc 指定
這通常是通過將對象的內部地址轉換為整數來實現的,但是 JavaTM 編程語言不需要這種實現技術。
如果一個類覆蓋了 hashCode,這意味着它想要生成一個特定的 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;
}
}
這也給出了正在初始化的類的實例數。
如果它是一個可以修改的類,則可以聲明一個類變量static java.util.concurrent.atomic.AtomicInteger nextInstanceId
。 (你必須以明顯的方式給它一個初始值。)然后聲明一個實例變量int instanceId = nextInstanceId.getAndIncrement()
。
我想出了這個解決方案,它適用於我在多個線程上創建對象並且可序列化的情況:
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())
我遇到了同樣的問題,並且對到目前為止的任何答案都不滿意,因為它們都不能保證唯一的 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 的任何對象都不會被垃圾回收。
由於我將其用於調試目的,因此我不太關心釋放的內存。
如果釋放內存是一個問題,您可以修改它以允許清除對象或刪除單個對象。
只是為了從不同的角度增加其他答案。
如果您想重用來自“above”的哈希碼並使用您的類的不可變狀態派生新的哈希碼,則調用 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");
}
}
}
由於Object.hashCode()
和System.identityHashCode()
不提供保證唯一的 ID,我認為正確的答案是生成 UUID 或 GUID:
java.util.UUID.randomUUID()
這個答案是線程安全的,並且可以跨不同的虛擬機工作。
例如, Identifiable
class 可以擴展如下,為任何 class 提供唯一 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
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.