Как получить уникальный идентификатор объекта, который переопределяет hashCode ()?

Когда класс в Java не отменяет hashCode (), печать экземпляра этого класса дает красивый уникальный номер.

В Javadoc Object говорится о hashCode ():

Насколько это разумно практично, метод hashCode, определенный классом Object, действительно возвращает отдельные целые числа для отдельных объектов.

Но когда класс переопределяет hashCode (), как я могу получить его уникальный номер?


person ivan_ivanovich_ivanoff    schedule 26.05.2009    source источник
comment
В основном по причинам «отладки»;) Чтобы иметь возможность сказать: Ах, тот же объект!   -  person ivan_ivanovich_ivanoff    schedule 26.05.2009
comment
Для этой цели, вероятно, пригодится System.identityHashcode (). Однако я бы не стал полагаться на него для реализации функциональности кода. Если вы хотите однозначно идентифицировать объекты, вы можете использовать AspectJ и code-weave с уникальным идентификатором для каждого созданного объекта. Хотя больше работы   -  person Brian Agnew    schedule 26.05.2009
comment
Просто имейте в виду, что уникальность hashCode НЕ гарантируется. Даже если реализация использует адрес памяти в качестве хэш-кода по умолчанию. Почему он не уникален? Потому что объекты собираются сборщиком мусора, а память используется повторно.   -  person Igor Krivokon    schedule 26.05.2009
comment
Обратите внимание на этот CR для документов API: bugs.sun.com/bugdatabase/view_bug.do ? bug_id = 6321873   -  person Tom Hawtin - tackline    schedule 26.05.2009
comment
Если вы хотите решить, являются ли два объекта одинаковыми, используйте == вместо hashCode (). Не гарантируется, что последний будет уникальным, даже в исходной реализации.   -  person Mnementh    schedule 26.05.2009
comment
Просто сгенерируйте свой собственный уникальный идентификатор во время создания экземпляра через static AtomicInteger()   -  person b1nary.atr0phy    schedule 09.06.2013
comment
Ни один из ответов не отвечает на настоящий вопрос, потому что они запутываются в обсуждении hashCode (), что здесь было случайным. Если я посмотрю на ссылочные переменные в Eclipse, он покажет мне уникальный неизменяемый id = xxx. Как мы можем получить это значение программно, не используя собственный генератор идентификаторов? Мне нужен доступ к этому значению для целей отладки (ведения журнала), чтобы идентифицировать отдельные экземпляры объектов. Кто-нибудь знает, как получить эту ценность?   -  person Chris Westin    schedule 09.04.2015
comment
@Mnementh для отладки иногда объекты равны, но вы все равно хотели бы знать, являются ли они разными объектами, возможно, лучше называть эти 2 объекта эквивалентными, а не одинаковыми - хотя я не уверен, что правильная терминология в компьютерном языке, но на английском языке это имеет наибольший смысл. В противном случае это может сбить с толку   -  person ycomp    schedule 08.11.2017
comment
@ycomp: в Java == сравнивает ссылки, что означает, что это верно только для одного и того же объекта. Для сравнения равенства следует использовать метод euqals (). Скажите a = новое целое число (1); b = новое целое число (1); a == b ложно, а a.equals (b) истинно.   -  person Mnementh    schedule 09.11.2017
comment
@Mnementh, извините, мои извинения, использование котлина в качестве основного языка действительно поджарило мне мозг, когда дело доходит до java (когда я устал)   -  person ycomp    schedule 09.11.2017
comment
Ваш уникальный идентификатор должен быть целочисленным / строковым идентификатором? Почему бы просто не использовать сам экземпляр Object? По сути, это уникальный указатель на все, что наследуется от объекта.   -  person MasterHD    schedule 28.06.2019


Ответы (10)


System.identityHashCode (yourObject) предоставит "исходный" хэш-код yourObject в виде целого числа. Уникальность не обязательно гарантируется. Реализация Sun JVM предоставит вам значение, связанное с исходным адресом памяти для этого объекта, но это деталь реализации, и вам не следует полагаться на нее.

РЕДАКТИРОВАТЬ: ответ изменен после комментария Тома ниже re. адреса памяти и движущиеся объекты.

person Brian Agnew    schedule 26.05.2009
comment
Дайте угадаю: это не уникально, когда у вас более 2 ** 32 объектов в одной JVM? ;) Можете указать мне какое-нибудь место, где описывается неуникальность? Спасибо! - person ivan_ivanovich_ivanoff; 26.05.2009
comment
Неважно, сколько там объектов или сколько памяти. Для создания уникального номера не требуется ни hashCode (), ни identityHashCode (). - person Alan Moore; 26.05.2009
comment
Брайан: Это не реальная ячейка памяти, вы случайно получаете повторно хешированную версию адреса при первом вычислении. В современной виртуальной машине объекты перемещаются в памяти. - person Tom Hawtin - tackline; 26.05.2009
comment
Итак, если объект создается по адресу памяти 0x2000, затем перемещается виртуальной машиной, а затем создается другой объект по адресу 0x2000, будет ли у них такой же System.identityHashCode()? - person lmat - Reinstate Monica; 08.06.2013
comment
Уникальность совершенно не гарантируется ... для практической реализации JVM. Гарантированная уникальность требует либо отсутствия перемещения / сжатия сборщиком мусора, либо большой и дорогостоящей структуры данных для управления значениями хэш-кода живых объектов. - person Stephen C; 24.07.2013
comment
Как тогда Object.hashChode() гарантированно останется неизменным в течение всего срока выполнения для одного и того же экземпляра. если этот API вернет то, что вернул бы «исходный» hashCode()? - person juanmf; 27.10.2020

Документ javadoc для объекта указывает, что

Обычно это реализуется путем преобразования внутреннего адреса объекта в целое число, но этот метод реализации не требуется для языка программирования JavaTM.

Если класс переопределяет hashCode, это означает, что он хочет сгенерировать определенный идентификатор, который (можно надеяться) будет иметь правильное поведение.

Вы можете использовать System .identityHashCode, чтобы получить этот идентификатор для любого класса.

person Valentin Rocher    schedule 26.05.2009

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;
    }
}
person ovunccetin    schedule 02.01.2015

Может быть, это быстрое грязное решение сработает?

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

    public int hashCode() {
        return uid;
    }
}

Это также дает количество экземпляров инициализируемого класса.

person John Pang    schedule 21.11.2014
comment
Это предполагает, что у вас есть доступ к исходному коду класса. - person pablisco; 20.01.2015
comment
Если вы не можете получить доступ к исходному коду, просто расширьте его и используйте расширенный класс. Просто быстрое, легкое и грязное решение, но оно работает. - person John Pang; 26.01.2015
comment
это не всегда работает. Занятие могло быть заключительным. Я думаю System.identityHashCode - лучшее решение - person pablisco; 26.01.2015
comment
Для обеспечения безопасности потоков можно использовать AtomicLong, как в этом ответе. - person Evgeni Sergeev; 11.03.2016
comment
Я прав, если класс загружается другим загрузчиком классов, у него будут разные статические переменные UNIQUE_ID? - person cupiqi09; 04.03.2020
comment
Этот ответ не предназначен для работы во всех случаях, например, с загрузчиком разных классов. Просто предложите простое и быстрое решение, которое может помочь. Если ваша ситуация более сложная, следует рассмотреть альтернативный ответ. Спасибо. - person John Pang; 19.03.2020
comment
Это не будет безопасным для потоков. т.е. может дать один и тот же uid двум разным объектам, если объекты созданы двумя одновременно работающими потоками. - person Volksman; 20.06.2021
comment
В Javadoc Object говорится о hashCode (): насколько это разумно практично, метод hashCode, определенный классом Object, действительно возвращает отдельные целые числа для отдельных объектов. Опять же, как написано в самом начале, это просто быстрое и грязное решение, которое МОЖЕТ помочь. Если ваша программа сложная (например, многопоточная или с другим загрузчиком классов), НЕ используйте ее. Для простой программы, требующей быстрого способа отладки, это МОЖЕТ (и НЕ МОЖЕТ) работать. - person John Pang; 22.06.2021

Если это класс, который вы можете изменить, вы можете объявить переменную класса static java.util.concurrent.atomic.AtomicInteger nextInstanceId. (Вам нужно будет очевидным образом указать ему начальное значение.) Затем объявите переменную экземпляра int instanceId = nextInstanceId.getAndIncrement().

person Aaron Mansheim    schedule 08.01.2016

Я придумал это решение, которое работает в моем случае, когда у меня есть объекты, созданные в нескольких потоках и сериализуемые:

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;
    }
}
person Howard Swope    schedule 09.12.2016

// looking for that last hex?
org.joda.DateTime@57110da6

Если вы изучаете hashcode типы Java, когда выполняете .toString() объект, основной код выглядит так:

Integer.toHexString(hashCode())
person Frankie    schedule 19.02.2019

У меня была такая же проблема, и пока я не был удовлетворен ни одним из ответов, поскольку ни один из них не гарантировал уникальные идентификаторы.

Я тоже хотел распечатать идентификаторы объектов для отладки. Я знал, что должен быть какой-то способ сделать это, потому что в отладчике 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);
        }
    }
}

Я считаю, что это должно гарантировать уникальные идентификаторы на протяжении всего срока действия программы. Однако обратите внимание, что вы, вероятно, не захотите использовать это в производственном приложении, потому что оно поддерживает ссылки на все объекты, для которых вы генерируете идентификаторы. Это означает, что любые объекты, для которых вы создаете идентификатор, никогда не будут собираться сборщиком мусора.

Поскольку я использую это в целях отладки, меня не слишком беспокоит освобождение памяти.

Вы можете изменить это, чтобы разрешить очистку объектов или удаление отдельных объектов, если освобождение памяти вызывает беспокойство.

person NateW    schedule 13.09.2016

Просто чтобы дополнить другие ответы под другим углом.

Если вы хотите повторно использовать хэш-коды «сверху» и получать новые, используя неизменяемое состояние вашего класса, тогда вызов super будет работать. Хотя это может / не может каскадировать полностью до объекта (т.е. какой-то предок может не вызывать super), это позволит вам получать хэш-коды путем повторного использования.

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

Существует разница между возвратами hashCode () и identityHashCode (). Возможно, что для двух неравных (проверенных с ==) объектов 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");
        }
    }
}
person Developer Marius Žilėnas    schedule 14.11.2014