How to get the unique ID of an object which overrides hashCode()?
Asked Answered
S

11

285

When a class in Java doesn't override hashCode(), printing an instance of this class gives a nice unique number.

The Javadoc of Object says about hashCode():

As much as is reasonably practical, the hashCode method defined by class Object does return distinct integers for distinct objects.

But when the class overrides hashCode(), how can I get its unique number?

Spickandspan answered 26/5, 2009 at 9:42 Comment(11)
Mostly for 'debugging' reasons ;) To be able to say: Ah, same object!Spickandspan
For this purpose the System.identityHashcode() is likely of some use. I wouldn't rely on it for implementing code functionality, however. If you want to identify objects uniquely, you could use AspectJ and code-weave in a unique id per created object. More work, thoughSubstantialism
Just keep in mind that hashCode is NOT guaranteed to be unique. Even if the implementaiton uses memory address as the default hashCode. Why is it not unique? Because objects get garbage collected, and memory is reused.Caduceus
Note this CR for API docs: bugs.sun.com/bugdatabase/view_bug.do?bug_id=6321873Encincture
If you want to decide, if two objects are the same use == instead of hashCode(). The latter is not guaranteed to be unique, even in the original implementation.Concavoconcave
Just generate your own unique ID during instantiation via a static AtomicInteger()Padishah
None of the answers answer the real question because they get tangled up in discussing hashCode(), which was incidental here. If I look at reference variables in Eclipse, it shows me a unique immutable "id=xxx." How do we get to that value programmatically without having to use our own id generator? I want access to that value for debugging purposes (logging) in order to identify distinct instances of objects. Does anybody know how to get your hands on that value?Gagliano
@Concavoconcave for debugging sometimes objects are equal but you'd still like to know if they are different objects, probably better to refer to those 2 objects as equivalent rather than the same - although I'm not sure the correct terminology in computer-speak but in English that makes the most sense. Otherwise it can be confusingKingston
@ycomp:In Java == compares references, which means it is only true for the same object. To compare the equalness, you should use the method euqals(). Say a= new Integer(1);b = new Integer(1); a==b is false while a.equals(b) is true.Concavoconcave
@Concavoconcave sorry my apologies, using kotlin as my main language really fried my brain when it comes to java (when I'm tired)Kingston
Does your unique identifier need to be an integer/String ID? Why not just use the Object instance itself? It's basically a unique pointer to anything that inherits from an object.Crossrefer
S
427

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.

EDIT: Answer modified following Tom's comment below re. memory addresses and moving objects.

Substantialism answered 26/5, 2009 at 9:46 Comment(6)
Let me guess: it's not unique, when you have more than 2**32 objects in same JVM? ;) Can you point me to some place, where the non-uniqueness it is described? Thanx!Spickandspan
It doesn't matter how many objects there are, or how much memory there is. Neither hashCode() nor identityHashCode() is required to produce a unique number.Efthim
Brian: It's not the actual memory location, you happen to get a rehashed version of an address when first computed. In a modern VM objects will move about in memory.Encincture
So if an object is created at memory address 0x2000, then is moved by the VM, then an other object is created at 0x2000, will they have the same System.identityHashCode()?Furnivall
Uniqueness isn't guaranteed at all ... for a practical JVM implementation. Guaranteed uniqueness requires either no relocation / compaction by the GC, or a big and expensive data structure for managing the hashcode values of live objects.Guardroom
How then is Object.hashChode() guaranteed to remain the same through out execution life for the same instance. if this API returns what the 'original' hashCode() would have returned?Bayberry
K
32

The javadoc for Object specifies that

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.

If a class overrides hashCode, it means that it wants to generate a specific id, which will (one can hope) have the right behaviour.

You can use System.identityHashCode to get that id for any class.

Konstanze answered 26/5, 2009 at 9:47 Comment(0)
N
12

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.

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.

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;
    }
}
Naseberry answered 2/1, 2015 at 11:40 Comment(0)
M
8

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.

Mendicant answered 21/11, 2014 at 22:48 Comment(9)
This assumes that you have access to the source code of the classInextinguishable
If you cannot access the source code, just extend from it and use the extended class. Simply quick, easy and dirty solution but it works.Mendicant
it doesn't always work. The class could be final. I think System.identityHashCode is a better solutionInextinguishable
For thread-safety, one could use AtomicLong as in this answer.Genuflect
If the class is loaded by a different classloader it will have different UNIQUE_ID static variables, am I correct?Holiness
This answer doesn't intended to work in all cases, such as different class loader. Just provide a simple, quick solution that may help. If your situation is more complicated, should consider alternative answer. Thanks.Mendicant
This won't be threadsafe. i.e. could give the same uid to two different objects if the objects are created by two concurrently running threads.Date
The Javadoc of Object says about hashCode(): "As much as is reasonably practical, the hashCode method defined by class Object does return distinct integers for distinct objects." Again, as written right at the beginning, this is just a quick and dirty solution that MAY help. If your program is complicated (such as multi-thread or with different class loader), please DO NOT use this. For a simple program and need a quick way for debugging, this MAY (and MAY NOT) work.Mendicant
This is exact may idea implement and used. Of course this is not the hashCode(). It is a special id. Hence I have offered an operation getId() { return uid; }, or more simple set a public final variable uid and access it. Of course this is only for one type of instances, but can be defined in a super type. I have for sorting graphic programming pins a base class Pin_..., derived pin classes, and all pins have a different unique id to detect same pin instances in several container. That is also supported by hashCode() but I have need it for combibations of pins, that's why the own solution.Salvadorsalvadore
G
7
// looking for that last hex?
org.joda.DateTime@57110da6

If you're looking into the hashcode Java types when you do a .toString() on an object the underlying code is this:

Integer.toHexString(hashCode())
Granulation answered 19/2, 2019 at 2:1 Comment(0)
U
5

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;
    }
}
Underestimate answered 9/12, 2016 at 21:22 Comment(0)
T
4

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().

Tonne answered 8/1, 2016 at 14:32 Comment(0)
D
2

I had the same issue and was not satisfied with any of the answers so far since none of them guaranteed unique IDs.

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.

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.

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.

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.

Decagon answered 13/9, 2016 at 21:28 Comment(0)
T
1

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.

@Override
public int hashCode() {
    int ancestorHash = super.hashCode();
    // now derive new hash from ancestorHash plus immutable instance vars (id fields)
}
Taxonomy answered 24/7, 2013 at 3:44 Comment(0)
T
1

Since Object.hashCode() and System.identityHashCode() do not provide IDs that are guaranteed to be unique, I think the correct answer is to generate a UUID or GUID:

java.util.UUID.randomUUID()

This answer is thread-safe and will work across different virtual machines.

For example, the Identifiable class could be extended as follows to provide any class with a unique 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
}
Thorson answered 23/11, 2022 at 13:55 Comment(0)
R
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.

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");
        }
    }
}
Regime answered 14/11, 2014 at 6:40 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.