hashCode ()를 재정의하는 객체의 고유 ID를 얻는 방법은 무엇입니까?


231

Java의 클래스가 hashCode ()를 재정의하지 않으면 이 클래스의 인스턴스를 인쇄하면 고유 한 숫자가 나타납니다.

Object의 Javadoc은 hashCode () 에 대해 말합니다 .

합리적으로 실용적이기는하지만 Object 클래스에 의해 정의 된 hashCode 메소드는 개별 오브젝트에 대해 고유 정수를 리턴합니다.

그러나 클래스가 hashCode ()를 재정의 하면 고유 번호를 어떻게 얻을 수 있습니까?


33
대부분 '파괴적인'이유 때문에;) 말할 수있다 : 아, 같은 물건!
ivan_ivanovich_ivanoff

5
이를 위해 System.identityHashcode ()가 다소 사용될 수 있습니다. 그러나 코드 기능을 구현하는 데 의존하지는 않습니다. 객체를 고유하게 식별하려면 AspectJ를 사용하고 생성 된 객체마다 고유 한 ID로 코드를 짜십시오. 더 많은 작업을하지만
브라이언 애그뉴

9
hashCode가 고유하다는 보장은 없습니다. 구현이 메모리 주소를 기본 해시 코드로 사용하더라도. 왜 독특하지 않습니까? 개체가 가비지 수집되고 메모리가 재사용되기 때문입니다.
Igor Krivokon

8
두 객체가 동일한 지 여부를 결정하려면 hashCode () 대신 ==를 사용하십시오. 후자는 원래 구현에서도 고유하지 않을 수 있습니다.
Mnementh

6
해시 코드 인 hashCode ()를 논의 할 때 답이 없기 때문에 실제 답변에 대한 답변은 없습니다. Eclipse에서 참조 변수를 보면 고유 한 불변의 "id = xxx"가 표시됩니다. 자체 ID 생성기를 사용하지 않고도 프로그래밍 방식으로 해당 값을 얻는 방법은 무엇입니까? 고유 한 객체 인스턴스를 식별하기 위해 디버깅 목적 (로깅)을 위해 해당 값에 액세스하려고합니다. 그 가치에 손을 대는 방법을 아는 사람이 있습니까?
Chris Westin

답변:


346

System.identityHashCode (yourObject)yourObject 의 '원래'해시 코드를 정수로 제공합니다. 독창성이 반드시 보장되는 것은 아닙니다. Sun JVM 구현은이 객체의 원래 메모리 주소와 관련된 값을 제공하지만 이는 구현 세부 사항이므로 의존해서는 안됩니다.

편집 : re 아래 Tom의 의견에 따라 수정 된 답변. 메모리 주소와 움직이는 물체.


같은 JVM에 2 ** 32 개 이상의 객체가있을 때 고유하지 않습니까? ;) 비고 유성이 설명되어있는 곳을 알려주시겠습니까? 고맙습니다!
ivan_ivanovich_ivanoff

9
얼마나 많은 객체가 있는지 또는 얼마나 많은 메모리가 있는지는 중요하지 않습니다. 고유 번호를 생성하는 데 hashCode () 또는 identityHashCode ()가 필요하지 않습니다.
Alan Moore

12
Brian : 실제 메모리 위치가 아니므로 처음 계산할 때 다시 해시 된 주소 버전이 나타납니다. 현대 VM에서 객체는 메모리에서 움직입니다.
Tom Hawtin-tackline

2
따라서 객체가 메모리 주소 0x2000에 생성 된 다음 VM에 의해 이동되면 다른 객체는 0x2000에 생성되며 동일한 것 System.identityHashCode()입니까?
제한 속죄

14
실용적인 JVM 구현을위한 독창성은 전혀 보장 되지 않습니다 . 고유성을 보장하려면 GC에 의한 재배치 / 압축이 필요하지 않거나 라이브 객체의 해시 코드 값을 관리하기위한 크고 비싼 데이터 구조가 필요합니다.
Stephen C

28

Object 용 javadoc은 다음을 지정합니다.

이는 일반적으로 오브젝트의 내부 주소를 정수로 변환하여 구현되지만이 구현 기술은 JavaTM 프로그래밍 언어에 필요하지 않습니다.

클래스가 hashCode를 재정의하면 특정 ID를 생성하고 싶다는 것을 의미합니다.

System.identityHashCode 를 사용 하여 모든 클래스의 해당 ID를 얻을 수 있습니다 .


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위해이 답변 과 같이 사용할 수 있습니다 .
Evgeni Sergeev

클래스가 다른 클래스 로더에 의해로드되면 다른 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당신이 할 자바 타입 .toString()객체에 기본 코드는 이것이다 :

Integer.toHexString(hashCode())

0

다른 대답을 다른 각도에서 보완하기 위해.

'위'에서 해시 코드를 재사용하고 클래스의 불변 상태를 사용하여 새 코드를 파생 시키려면 super에 대한 호출이 작동합니다. 이것은 Object까지 완전히 계단식으로 배열되지 않을 수도 있지만 (즉, 일부 조상은 super를 호출하지 않을 수도 있음) 재사용을 통해 해시 코드를 파생시킬 수 있습니다.

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

0

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

0

나는 동일한 문제가 있었고 지금까지 아무도 고유 ID를 보장하지 않았기 때문에 어떤 대답에도 만족하지 못했습니다.

디버깅 목적으로 객체 ID를 인쇄하고 싶었습니다. Eclipse 디버거에서 각 객체의 고유 ID를 지정하기 때문에 어떤 방법이 있어야한다는 것을 알고있었습니다.

두 객체가 실제로 동일한 인스턴스 인 경우 객체에 대한 "=="연산자 만 true를 반환한다는 사실에 기반한 솔루션을 생각해 냈습니다.

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

이것이 프로그램의 수명 기간 동안 고유 한 ID를 보장해야한다고 생각합니다. 그러나 ID를 생성하는 모든 객체에 대한 참조를 유지하므로 프로덕션 응용 프로그램에서 이것을 사용하지 않을 수도 있습니다. 즉, ID를 생성하는 모든 객체는 가비지 수집되지 않습니다.

디버그 목적으로 이것을 사용하고 있기 때문에 해제되는 메모리에 대해서는별로 신경 쓰지 않습니다.

메모리를 확보해야하는 경우 오브젝트를 지우거나 개별 오브젝트를 제거 할 수 있도록이를 수정할 수 있습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.