Jak uzyskać unikalny identyfikator obiektu, który nadpisuje hashCode()?

Gdy klasa w Javie nie nadpisuje hashCode () , drukowanie instancji tej klasy daje ładny unikalny numer.

Javadoc obiektu mówi o hashCode():

o ile jest to w miarę praktyczne, metoda hashCode zdefiniowana przez obiekt klasy zwraca różne liczby całkowite dla różnych obiektów.

Ale gdy Klasa nadpisuje hashCode () , Jak mogę uzyskać to unikalny numer?

Author: John Kugelman, 2009-05-26

9 answers

System.identityHashCode (yourObject) poda 'oryginalny' kod hashowy yourObject jako liczbę całkowitą. Wyjątkowość niekoniecznie jest gwarantowana. Implementacja Sun JVM da ci wartość, która jest związana z oryginalnym adresem pamięci dla tego obiektu, ale jest to szczegół implementacji i nie powinieneś na nim polegać.

EDIT: odpowiedź zmodyfikowana po komentarzu Toma pod re. adresy pamięci i poruszające się obiekty.

 295
Author: Brian Agnew,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-10-18 22:51:21

Obiekt javadoc for określa, że

Jest to zazwyczaj implementowane przez konwersję wewnętrznego adresu obiektu na liczbę całkowitą, ale ta technika implementacji nie jest wymagana przez język programowania JavaTM.

Jeśli Klasa nadpisuje hashCode, oznacza to, że chce wygenerować określone id, które będzie (można mieć nadzieję) miało właściwe zachowanie.

Możesz użyć systemu.identityHashCode , aby uzyskać ten identyfikator dla dowolnej klasy.

 27
Author: Valentin Rocher,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-06-28 12:37:40

Może to szybkie, brudne rozwiązanie zadziała?

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

    public int hashCode() {
        return uid;
    }
}

To również podaje liczbę instancji inicjalizowanej klasy.

 7
Author: John Pang,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-11-21 22:48:01

hashCode() metoda nie służy do podania unikalnego identyfikatora obiektu. To raczej trawi stan obiektu (tzn. wartości pól członkowskich) do pojedynczej liczby całkowitej. Ta wartość jest najczęściej używana przez niektóre struktury danych oparte na hashach, takie jak Mapy i zestawy, aby skutecznie przechowywać i pobierać obiekty.

Jeśli potrzebujesz identyfikatora dla swoich obiektów, zalecam dodanie własnej metody zamiast nadpisywania hashCode. W tym celu można utworzyć interfejs bazowy (lub klasę abstrakcyjną), jak poniżej.

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

Przykładowe użycie:

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

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

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

Dla generowania id możesz sprawdzić mój wpis na blogu że próbowałem wyjaśnić kilka sposobów generowania unikalnych ID.

 7
Author: ovunccetin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-01-05 12:33:36

Jeśli jest to klasa, którą możesz zmodyfikować, możesz zadeklarować zmienną klasy static java.util.concurrent.atomic.AtomicInteger nextInstanceId. (Musisz podać jej wartość początkową w oczywisty sposób.) Następnie zadeklaruj zmienną instancji int instanceId = nextInstanceId.getAndIncrement().

 3
Author: Aaron Mansheim,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-01-08 14:32:53

Wymyśliłem takie rozwiązanie, które działa w moim przypadku, gdy mam obiekty utworzone na wielu wątkach i są serializowalne:

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
Author: Howard Swope,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-12-09 21:22:15

Żeby poszerzyć Pozostałe odpowiedzi pod innym kątem.

Jeśli chcesz ponownie użyć hashcode z 'above' i wyprowadzić nowe używając stanu immutatable twojej klasy, wtedy wywołanie super będzie działać. Chociaż może to / nie może być kaskadowe aż do obiektu (np. jakiś przodek może nie wywoływać super), pozwoli Ci to uzyskać hashcodes przez ponowne użycie.

@Override
public int hashCode() {
    int ancestorHash = super.hashCode();
    // now derive new hash from ancestorHash plus immutable instance vars (id fields)
}
 0
Author: Glen Best,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-07-24 03:44:43

Istnieje różnica między zwrotami hashCode() i identityHashCode (). Jest możliwe, że dla dwóch nierównych (testowanych z ==) obiektów O1, O2 hashCode() może być taki sam. Zobacz poniższy przykład, jak to jest prawdą.

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
Author: Willmore,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-11-14 06:40:39

Miałem ten sam problem i nie byłem usatysfakcjonowany żadną z odpowiedzi, ponieważ żadna z nich nie gwarantowała unikalnych identyfikatorów.

Ja też chciałem wydrukować ID obiektów do debugowania. Wiedziałem, że musi być na to jakiś sposób, ponieważ w Debuggerze Eclipse określa on unikalne identyfikatory dla każdego obiektu.

Wymyśliłem rozwiązanie oparte na fakcie, że operator "= = " dla obiektów zwraca true tylko wtedy, gdy oba obiekty są rzeczywiście tą samą instancją.

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

Wierzę, że powinno to zapewnić unikalne identyfikatory przez cały okres użytkowania programu. Zauważ jednak, że prawdopodobnie nie chcesz tego używać w aplikacji produkcyjnej, ponieważ przechowuje ona odniesienia do wszystkich obiektów, dla których generujesz identyfikatory. Oznacza to, że obiekty, dla których utworzysz ID, nigdy nie będą zbierane jako śmieci.

Ponieważ używam tego do celów debugowania, nie martwię się zbytnio o uwolnienie pamięci.

Można to zmodyfikować, aby umożliwić czyszczenie obiektów lub usuwanie poszczególnych obiektów, jeśli uwolnienie pamięci jest problemem.

 0
Author: NateW,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-09-13 21:28:47