HashCode () öğesini geçersiz kılan bir nesnenin benzersiz kimliğini nasıl edinebilirim?


231

Java'daki bir sınıf hashCode () yöntemini geçersiz kılmadığında , bu sınıfın bir örneğini yazdırmak hoş bir benzersiz sayı verir.

Object Javadoc hashCode () hakkında şunları söylüyor :

Oldukça pratik olduğu kadar, Object sınıfı tarafından tanımlanan hashCode yöntemi farklı nesneler için farklı tamsayılar döndürür.

Ancak sınıf hashCode () öğesini geçersiz kıldığında , benzersiz numarasını nasıl alabilirim?


33
Çoğunlukla 'hata ayıklama' nedenleriyle;) Söyleyebileceğim: Ah, aynı nesne!
ivan_ivanovich_ivanoff

5
Bu amaçla System.identityHashcode () yöntemi büyük olasılıkla bir miktar kullanılabilir. Bununla birlikte, kod işlevselliğini uygulamak için buna güvenmezdim. Nesneleri benzersiz bir şekilde tanımlamak istiyorsanız, oluşturulan nesne başına benzersiz bir kimlikte AspectJ ve kod örgüsünü kullanabilirsiniz. Yine de daha fazla iş
Brian Agnew

9
HashCode'un benzersiz olduğu garanti EDİLMEDİĞİNİ unutmayın. Uygulama varsayılan hashCode olarak bellek adresini kullanıyor olsa bile. Neden benzersiz değil? Çünkü nesneler çöp toplar ve bellek yeniden kullanılır.
Igor Krivokon

8
Karar vermek istiyorsanız, iki nesne aynı ise hashCode () yerine == kullanın. İkincisinin, orijinal uygulamada bile benzersiz olduğu garanti edilmez.
Mnementh

6
Cevapların hiçbiri gerçek soruyu cevaplamaz, çünkü burada tesadüfi olan hashCode () 'u tartışmaya karışırlar. Eclipse referans değişkenlerine bakarsam, bana benzersiz bir değişmez "id = xxx" gösterir. Kendi kimlik üretecimizi kullanmak zorunda kalmadan bu değere programlı olarak nasıl ulaşabiliriz? Nesnelerin farklı örneklerini tanımlamak için hata ayıklama amacıyla (günlük kaydı) bu değere erişmek istiyorum. Bu değeri nasıl elde edebileceğinizi bilen var mı?
Chris Westin

Yanıtlar:


346

System.identityHashCode (yourObject) , bir tamsayı olarakObject öğenizin 'orijinal' karma kodunu verecektir. Benzersizlik mutlaka garanti edilmez. Sun JVM uygulaması size bu nesnenin orijinal bellek adresiyle ilişkili bir değer verecektir, ancak bu bir uygulama detayıdır ve buna güvenmemelisiniz.

DÜZENLEME: Yanıt, Tom'un aşağıdaki yorumundan sonra değiştirildi. bellek adresleri ve hareketli nesneler.


Tahmin edeyim: Aynı JVM'de 2 ** 32'den fazla nesneniz olduğunda benzersiz değil mi? ;) Eşsizliğin tarif edildiği bir yere yönlendirebilir misiniz? Thanx!
ivan_ivanovich_ivanoff

9
Kaç tane nesne olduğu veya ne kadar bellek olduğu önemli değildir. Benzersiz bir sayı üretmek için ne hashCode () ne de identityHashCode () gerekmez.
Alan Moore

12
Brian: Bu gerçek bellek konumu değil, ilk hesaplandığında adresin yeniden düzenlenmiş bir versiyonunu alıyorsunuz. Modern bir VM'de nesneler bellekte hareket edecektir.
Tom Hawtin - taktik

2
Yani 0x2000 bellek adresinde bir nesne oluşturulursa, VM tarafından taşınırsa, 0x2000'de başka bir nesne oluşturulur, aynı olacak System.identityHashCode()mı?
Sınırlı Kefaret

14
Pratik bir JVM uygulaması için benzersizlik hiç garanti edilmez . Garantili benzersizlik, GC tarafından herhangi bir yer değiştirme / sıkıştırma gerektirmez veya canlı nesnelerin hashcode değerlerini yönetmek için büyük ve pahalı bir veri yapısı gerektirir.
Stephen C

28

Object için javadoc,

Bu genellikle nesnenin dahili adresini bir tamsayıya dönüştürerek uygulanır, ancak bu uygulama tekniği JavaTM programlama dili için gerekli değildir.

Bir sınıf hashCode'u geçersiz kılarsa, belirli bir kimliği oluşturmak istediği anlamına gelir;

Herhangi bir sınıf için bu kimliği almak için System.identityHashCode kullanabilirsiniz .


7

hashCode()yöntemi, bir nesne için benzersiz bir tanımlayıcı sağlamak değildir. Bunun yerine, nesnenin durumunu (üye alanlarının değerleri) tek bir tamsayıya sindirir. Bu değer çoğunlukla nesneleri etkili bir şekilde saklamak ve almak için haritalar ve kümeler gibi karma tabanlı veri yapıları tarafından kullanılır.

Nesneleriniz için bir tanımlayıcıya ihtiyacınız varsa, geçersiz kılmak yerine kendi yönteminizi eklemenizi öneririm hashCode. Bu amaçla, aşağıdaki gibi bir temel arayüz (veya soyut bir sınıf) oluşturabilirsiniz.

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

Örnek kullanım:

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

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

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

6

Belki bu hızlı, kirli çözüm işe yarar?

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

    public int hashCode() {
        return uid;
    }
}

Bu aynı zamanda başlatılan bir sınıfın örnek sayısını verir.


4
Bu, sınıfın kaynak koduna erişiminiz olduğunu varsayar
pablisco

Kaynak koduna erişemiyorsanız, kodu genişletin ve genişletilmiş sınıfı kullanın. Basit, hızlı, kolay ve kirli bir çözüm ama işe yarıyor.
John Pang

1
her zaman işe yaramaz. Sınıf nihai olabilir. Bence System.identityHashCodedaha iyi bir çözüm
pablisco

2
Parçacığı güvenliği için, tek kullanabilirsiniz AtomicLongolduğu gibi bu cevap .
Evgeni Sergeev

Sınıf farklı bir sınıf yükleyici tarafından yüklenirse farklı UNIQUE_ID statik değişkenleri olacaktır, doğru muyum?
cupiqi09

4

Eğer değiştirebileceğiniz bir sınıfsa, bir sınıf değişkeni bildirebilirsiniz static java.util.concurrent.atomic.AtomicInteger nextInstanceId. (Açık bir şekilde ona bir başlangıç ​​değeri vermeniz gerekir.) Ardından bir örnek değişkeni bildirin int instanceId = nextInstanceId.getAndIncrement().


2

Ben birden çok iş parçacıkları üzerinde oluşturulan nesneler var ve serileştirilebilir benim durumumda çalışan bu çözüm ile geldi:

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

Bir nesne üzerinde bir hashcodeJava yaptığınızda Java türlerine bakıyorsanız .toString(), temel kod şudur:

Integer.toHexString(hashCode())

0

Diğer cevapları farklı bir açıdan arttırmak için.

Eğer 'yukarıdan' hashcode (ları) tekrar kullanmak ve sınıfınızın değişmez durumunu kullanarak yenilerini türetmek istiyorsanız, super çağrısı işe yarayacaktır. Bu, Nesneye kadar kademeli olarak sıralanabilse de (örneğin, bazı atalar süper çağırmayabilir), ancak hash kodlarını yeniden kullanarak türetmenize izin verecektir.

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

0

HashCode () ve identityHashCode () döndürmeleri arasında bir fark vardır. İki eşit olmayan (== ile test edilmiş) o1 nesnesi için o2 hashCode () aynı olabilir. Bunun nasıl doğru olduğu aşağıdaki örneğe bakın.

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

Aynı sorunu yaşadım ve hiçbiri benzersiz kimlikleri garanti etmediği için şimdiye kadar cevapların hiçbirinden memnun kalmadım.

Ben de amaçlanan hata ayıklama için nesne kimlikleri yazdırmak istedim. Eclipse hata ayıklayıcısında, her nesne için benzersiz kimlikler belirlediğinden, bunu yapmanın bir yolu olması gerektiğini biliyordum.

Ben nesneler için "==" operatörü sadece iki nesne aslında aynı örnek ise doğru döndürür dayalı bir çözüm geldi.

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

Bunun, programın ömrü boyunca benzersiz kimlikler sağlaması gerektiğine inanıyorum. Bununla birlikte, büyük olasılıkla bunu bir üretim uygulamasında kullanmak istemediğinize dikkat edin, çünkü kimlikleri oluşturduğunuz tüm nesnelere referansları korur. Bu, bir kimlik oluşturduğunuz nesnelerin hiçbir zaman çöp toplanmayacağı anlamına gelir.

Bunu hata ayıklama amacıyla kullandığım için, hafızanın boşaltılmasıyla fazla ilgilenmiyorum.

Belleği boşaltmak bir sorun oluşturuyorsa, nesneleri temizlemeye veya nesneleri tek tek kaldırmaya izin vermek için bunu değiştirebilirsiniz.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.