Як отримати унікальний ідентифікатор об’єкта, який переосмислює hashCode ()?


231

Коли клас у Java не перекриває hashCode () , друк примірника цього класу дає приємне унікальне число.

Javadoc Object говорить про hashCode () :

Наскільки це практично практично, метод hashCode, визначений класом Object, повертає окремі цілі числа для різних об'єктів.

Але коли клас замінює hashCode () , як я можу отримати його унікальний номер?


33
Здебільшого з причин налагодження;) щоб можна було сказати: А, той самий об’єкт!
ivan_ivanovich_ivanoff

5
Для цього System.identityHashcode (), ймовірно, буде певним. Я б не покладався на нього для реалізації функціональності коду. Якщо ви хочете унікально ідентифікувати об'єкти, ви можете використовувати AspectJ та переплетення коду в унікальний ідентифікатор на створений об’єкт. Більше роботи, хоча
Брайан Агнеу

9
Пам’ятайте лише про те, що hashCode НЕ гарантовано є унікальним. Навіть якщо впровадження використовує адресу пам'яті як хеш-код за замовчуванням. Чому він не унікальний? Тому що предмети збирають сміття, а пам'ять повторно використовується.
Ігор Кривоконь

8
Якщо ви хочете вирішити, чи два об'єкти однакові, використовуйте == замість hashCode (). Останнє не гарантовано буде унікальним навіть у оригінальній реалізації.
Мнемент

6
Жодна з відповідей не відповідає на справжнє запитання, оскільки вони заплутуються під час обговорення hashCode (), що тут було випадковим. Якщо я дивлюсь на еталонні змінні в Eclipse, це показує мені унікальний незмінний "id = xxx". Як ми можемо досягти цього значення програмно, не використовуючи власний генератор ідентифікаторів? Я хочу отримати доступ до цього значення для налагодження (ведення журналу), щоб виявити окремі екземпляри об'єктів. Хтось знає, як домогтися цієї цінності?
Кріс Вестін

Відповіді:


346

System.identityHashCode (yourObject) надасть 'оригінальний' хеш-код вашогоObject у вигляді цілого числа. Унікальність не обов'язково гарантована. Реалізація Sun JVM дасть вам значення, яке пов'язане з оригінальною адресою пам'яті для цього об’єкта, але це деталізація реалізації, і ви не повинні покладатися на неї.

EDIT: Відповідь змінено після коментаря Тома нижче пере. адреси пам'яті та рухомі об’єкти.


Дозвольте здогадатися: це не унікально, коли у вас є більше 2 ** 32 об’єктів в одному JVM? ;) Чи можете ви вказати мені на якесь місце, де описана неповторність? Дякую!
ivan_ivanovich_ivanoff

9
Не має значення, скільки об’єктів є, або скільки пам’яті. Ні хеш-код (), ні ідентифікаціяHashCode () не потрібні для створення унікального номера.
Алан Мур

12
Брайан: Це не фактичне місце в пам'яті, ви, коли вперше отримаєте перероблену версію адреси при першому обчисленні. У сучасному VM об’єкти будуть рухатися по пам’яті.
Том Хотін - тайклін

2
Отже, якщо об’єкт створений за адресою пам’яті 0x2000, то переміщується VM, тоді інший об’єкт створюється на 0x2000, чи будуть вони такі самі System.identityHashCode()?
Обмежене спокутування

14
Унікальність взагалі не гарантована ... для практичної реалізації JVM. Гарантована унікальність не потребує або переїзду / ущільнення GC, або великої та дорогої структури даних для управління значеннями хеш-коду живих об'єктів.
Стівен С

28

Явадок для Object вказує це

Це, як правило, реалізується шляхом перетворення внутрішньої адреси об'єкта в ціле число, але ця технологія реалізації не потрібна мові програмування JavaTM.

Якщо клас переосмислює hashCode, це означає, що він хоче сформувати певний ідентифікатор, який (можна сподіватися) мати правильну поведінку.

Ви можете використовувати System.identityHashCode, щоб отримати цей ідентифікатор для будь-якого класу.


7

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

6

Може, це швидке, брудне рішення спрацює?

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

    public int hashCode() {
        return uid;
    }
}

Це також дає кількість екземплярів ініціалізованого класу.


4
Це передбачає, що у вас є доступ до вихідного коду класу
pablisco

Якщо ви не можете отримати доступ до вихідного коду, просто продовжте його та використовуйте розширений клас. Просто швидке, легке та брудне рішення, але воно працює.
Джон Панг

1
це не завжди працює. Клас може бути заключним. Я думаю System.identityHashCode, що краще рішення
pablisco

2
Для безпеки потоку можна використовувати, AtomicLongяк у цій відповіді .
Євгеній Сергєєв

Якщо клас завантажений іншим завантажувачем класів, він матиме різні статичні змінні UNIQUE_ID, я прав?
cupiqi09

4

Якщо це клас, який ви можете змінити, ви можете оголосити змінну класу static java.util.concurrent.atomic.AtomicInteger nextInstanceId. (Вам доведеться надати йому початкове значення очевидним чином.) Потім оголосіть змінну екземпляра int instanceId = nextInstanceId.getAndIncrement().


2

Я придумав таке рішення, яке працює в моєму випадку, коли у мене є об'єкти, створені на декількох потоках і які можна серіалізувати:

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
// looking for that last hex?
org.joda.DateTime@57110da6

Якщо ви шукаєте hashcodeтипи Java, коли ви робите .toString()на об'єкті, базовим кодом є такий:

Integer.toHexString(hashCode())

0

Просто для збільшення інших відповідей з іншого кута.

Якщо ви хочете повторно використовувати хеш-код (-ів) зверху «зверху» та отримати нові, використовуючи незмінний стан вашого класу, тоді виклик супер буде спрацьовувати. Хоча це може / не може каскадувати аж до Об'єкта (тобто якийсь пращур може не називати супер), це дозволить отримати хеш-коди шляхом повторного використання.

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

0

Існує різниця між поверненнями hashCode () та IdentHashCode (). Можливо, що для двох неоднакових (тестованих = =) об’єктів 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");
        }
    }
}

0

У мене було те саме питання, і я не задоволений жодною з відповідей поки що, оскільки жодна з них не гарантувала унікальні посвідчення особи.

Я теж хотів надрукувати ідентифікатори об’єктів для призначеної налагодження. Я знав, що повинен бути якийсь спосіб це зробити, тому що в налагоджувачі Eclipse він вказує унікальні ідентифікатори для кожного об'єкта.

Я придумав рішення, засноване на тому, що оператор "==" для об'єктів повертає істину лише у тому випадку, якщо два об'єкти насправді однакові.

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

Я вважаю, що це повинно забезпечити унікальні посвідчення особи протягом усього життя програми. Однак зауважте, що ви, ймовірно, не хочете використовувати це у виробничому додатку, оскільки він підтримує посилання на всі об'єкти, для яких ви генеруєте ідентифікатори. Це означає, що будь-які об’єкти, для яких ви створюєте ідентифікатор, ніколи не збиратимуть сміття.

Оскільки я використовую це для цілей налагодження, я не надто переймаюся тим, що звільняється пам'ять.

Ви можете змінити це, щоб дозволити очищення об'єктів або видалення окремих об'єктів, якщо звільнення пам'яті викликає занепокоєння.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.