it-swarm.com.de

Wie erhalte ich die eindeutige ID eines Objekts, das hashCode () überschreibt?

Wenn eine Klasse in Java hashCode () nicht überschreibt, gibt das Drucken einer Instanz dieser Klasse eine eindeutige Nizza-Nummer.

Das Javadoc von Object sagt über hashCode ():

Soweit es sinnvoll ist, gibt die von der Klasse Object definierte hashCode-Methode unterschiedliche Ganzzahlen für unterschiedliche Objekte zurück.

Aber wenn die Klasse hashCode () überschreibt, wie kann ich seine eindeutige Nummer erhalten?

215

System.identityHashCode (yourObject) gibt den 'ursprünglichen' Hash-Code von yourObject als Ganzzahl an. Die Einzigartigkeit ist nicht unbedingt garantiert. Die Sun JVM-Implementierung gibt Ihnen einen Wert, der sich auf die ursprüngliche Speicheradresse für dieses Objekt bezieht. Dies ist jedoch ein Implementierungsdetail, auf das Sie sich nicht verlassen sollten.

BEARBEITEN: Antwort geändert nach Toms Kommentar unten bezüglich. Speicheradressen und bewegte Objekte.

324
Brian Agnew

Das Javadoc für Object gibt das an

Dies wird normalerweise durch Konvertieren der internen Adresse des Objekts in eine Ganzzahl implementiert. Diese Implementierungstechnik wird jedoch von der Programmiersprache JavaTM nicht benötigt.

Wenn eine Klasse hashCode überschreibt, bedeutet dies, dass sie eine bestimmte ID generieren möchte, die (wie man hoffen kann) das richtige Verhalten aufweist.

Sie können System.identityHashCode verwenden, um diese ID für jede Klasse abzurufen.

28
Valentin Rocher

Vielleicht funktioniert diese schnelle, schmutzige Lösung?

public class A {
    static int UNIQUE_ID = 0;
    int uid = ++UNIQUE_ID;

    public int hashCode() {
        return uid;
    }
}

Dies gibt auch die Nummer der Instanz einer Klasse an, die initialisiert wird.

8
John Pang

Die hashCode() -Methode dient nicht zur Bereitstellung einer eindeutigen Kennung für ein Objekt. Vielmehr wird der Status des Objekts (d. H. Die Werte der Mitgliedsfelder) in eine einzelne Ganzzahl zerlegt. Dieser Wert wird hauptsächlich von einigen Hash-basierten Datenstrukturen wie Maps und Sets verwendet, um Objekte effektiv zu speichern und abzurufen.

Wenn Sie einen Bezeichner für Ihre Objekte benötigen, empfehle ich Ihnen, Ihre eigene Methode hinzuzufügen, anstatt hashCode zu überschreiben. Zu diesem Zweck können Sie eine Basisschnittstelle (oder eine abstrakte Klasse) wie folgt erstellen.

public interface IdentifiedObject<I> {
    I getId();
}

Anwendungsbeispiel:

public class User implements IdentifiedObject<Integer> {
    private Integer studentId;

    public User(Integer studentId) {
        this.studentId = studentId;
    }

    @Override
    public Integer getId() {
        return studentId;
    }
}

Zur ID-Generierung können Sie meinen Blog-Beitrag überprüfen, dass ich versucht habe, einige Möglichkeiten zum Generieren eindeutiger IDs zu erläutern.

7
ovunccetin

Wenn es sich um eine Klasse handelt, die Sie ändern können, können Sie eine Klassenvariable static Java.util.concurrent.atomic.AtomicInteger nextInstanceId Deklarieren. (Sie müssen ihm auf offensichtliche Weise einen Anfangswert zuweisen.) Deklarieren Sie dann eine Instanzvariable int instanceId = nextInstanceId.getAndIncrement().

3
Aaron Mansheim

Ich habe diese Lösung gefunden, die in meinem Fall funktioniert, in dem Objekte auf mehreren Threads erstellt wurden und serialisierbar sind:

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;
    }
}
2
Howard Swope
// looking for that last hex?
[email protected]

Wenn Sie sich mit den hashcode Java) - Typen beschäftigen, wenn Sie .toString() für ein Objekt ausführen, lautet der zugrunde liegende Code wie folgt:

Integer.toHexString(hashCode())
1
Frankie

Es gibt einen Unterschied zwischen den Ergebnissen von hashCode () und identityHashCode (). Es ist möglich, dass für zwei ungleiche (mit == getestete) Objekte o1, o2 hashCode () gleich sein kann. Sehen Sie sich das folgende Beispiel an, wie das stimmt.

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");
        }
    }
}
0
display_name

Nur um die anderen Antworten aus einem anderen Blickwinkel zu erweitern.

Wenn Sie Hashcodes von "oben" wiederverwenden und neue mithilfe des unveränderlichen Status Ihrer Klasse ableiten möchten, funktioniert ein Aufruf von "super". Dies kann/kann nicht bis zum Objekt durchlaufen (d. H. Ein Vorfahr ruft möglicherweise nicht super auf), ermöglicht es Ihnen jedoch, Hashcodes durch Wiederverwendung abzuleiten.

@Override
public int hashCode() {
    int ancestorHash = super.hashCode();
    // now derive new hash from ancestorHash plus immutable instance vars (id fields)
}
0
Glen Best

Ich hatte das gleiche Problem und war bisher mit keiner der Antworten zufrieden, da keine von ihnen eindeutige IDs garantierte.

Auch ich wollte Objekt-IDs zum Debuggen ausdrucken. Ich wusste, dass es dafür eine Möglichkeit geben musste, da im Eclipse-Debugger eindeutige IDs für jedes Objekt angegeben werden.

Ich habe eine Lösung gefunden, die auf der Tatsache basiert, dass der Operator "==" für Objekte nur dann true zurückgibt, wenn die beiden Objekte tatsächlich dieselbe Instanz sind.

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);
        }
    }
}

Ich glaube, dass dies eindeutige IDs während der gesamten Laufzeit des Programms sicherstellen sollte. Beachten Sie jedoch, dass Sie dies wahrscheinlich nicht in einer Produktionsanwendung verwenden möchten, da es Verweise auf alle Objekte verwaltet, für die Sie IDs generieren. Dies bedeutet, dass Objekte, für die Sie eine ID erstellen, niemals über den Müll gesammelt werden.

Da ich dies für Debug-Zwecke verwende, kümmere ich mich nicht allzu sehr darum, dass der Speicher freigegeben wird.

Sie können dies ändern, um das Löschen von Objekten oder das Entfernen einzelner Objekte zu ermöglichen, wenn die Freigabe von Speicher ein Problem darstellt.

0
NateW