So is it only in extreme cases like this where hashcodes would be
duplicated?
Not necessarily, but it's unlikely you'll run out of unique ones in a
production environment.
I suggest you use System.identityHashCode(Object) to get these
numbers. It should be a) fixed for an object's lifetime in one session
(it will change when the object is serialized and later deserialized);
b) globally unique (within the one JVM anyway) as the usual
implementation of the default hash code for Object is the memory
address of that object, which is necessarily globally unique in that
scope; and c) not subject to being overridden unlike calling
hashCode() on the object. This of course works if you need a globally
unique ID per object, even to the point of two copies of a single
object (so equals()) have distinct such IDs. Try to just use hashCode
otherwise.
A second option is to create an IdentityHashMap<Object, Integer> and
stuff objects into that. Distinct objects act as distinct keys (even
if equals()) and you'd assign a new higher integer to each one. Use
Long if you run out of Integers (unlikely). Use BigDecimal if you run
out of Longs (unlikely until we have converted most of the visible
universe into computronium). This has the benefit of giving out ever-
higher numbers even to objects that use the memory space where an
earlier object was before being garbage collected. You can't twiddle
the Object constructor to put everything in automatically, but you can
fake it by having the get-ID method you plan to use actually assign an
ID to objects that don't already have one lazily when it's first
requested. Of course, the objects won't get garbage collected unless
you use a WeakReference, in which case you may as well use an ordinary
HashMap<WeakReference<Object>, Integer> as the WeakReference hashCode
is the default one if I recall correctly. (WeakHashMap would cause
distinct objects that compare equal to have the same ID.) This is
complicated because you need to carry the WeakReference around with
you to look up in the HashMap (you can't just make a new one to the
same object and expect it to work). Perhaps a better option is to wrap
objects that will need IDs in a dummy object that has a single public
Object field, the default equals and hashCode, and make a
WeakHashMap<Dummy, Integer>. The wrapper object has to be used in
place of the original object on any path that leads to getting its ID.
Finally, if this ID is only needed for objects of classes you control,
you can make the base classes you control generate unique ID numbers
to put in a public final field. This is easiest if you can derive all
the classes for which you have this requirement from a single base.
Otherwise, the ID can be a long with 32 bits a sequentially-assigned
int and 32 bits the hashCode of the particular base class.