Bagaimana Anda mendapatkan "referensi objek" dari sebuah objek di java ketika toString () dan hashCode () telah diganti?

106

Saya ingin mencetak "referensi objek" dari suatu objek di Java untuk keperluan debugging. Yaitu untuk memastikan bahwa objeknya sama (atau berbeda) tergantung situasinya.

Masalahnya adalah bahwa kelas tersebut mewarisi dari kelas lain, yang telah menimpa toString () dan hashCode () yang biasanya akan memberi saya id.

Contoh situasi: Menjalankan aplikasi multi-utas, di mana saya (selama pengembangan) ingin memeriksa apakah semua utas menggunakan contoh yang sama dari objek sumber daya atau tidak.

Nicolai
sumber
1
tergantung pada apakah Anda dapat melakukannya sama sekali ... == adalah cara yang harus dilakukan ... tetapi saya tidak tahu bagaimana kode tersebut disusun. Sekali lagi, hashCode mungkin baik-baik saja untuk apa yang Anda lakukan, tetapi itu bisa rusak tergantung pada bagaimana perpustakaan diimplementasikan.
TofuBeer
Ini benar-benar pertanyaan yang bagus.
Zhang Xiang

Jawaban:

108

Apa sebenarnya yang Anda rencanakan untuk dilakukan dengannya (apa yang ingin Anda lakukan membuat perbedaan dengan apa yang perlu Anda panggil).

hashCode, seperti yang didefinisikan dalam JavaDocs, mengatakan:

Sebanyak praktis, metode hashCode yang didefinisikan oleh Objek kelas tidak mengembalikan bilangan bulat berbeda untuk objek berbeda. (Ini biasanya diimplementasikan dengan mengubah alamat internal objek menjadi integer, tetapi teknik implementasi ini tidak diperlukan oleh bahasa pemrograman Java ™.)

Jadi, jika Anda menggunakan hashCode()untuk mencari tahu apakah itu adalah objek unik dalam memori, itu bukan cara yang baik untuk melakukannya.

System.identityHashCode melakukan hal berikut:

Mengembalikan kode hash yang sama untuk objek yang diberikan seperti yang akan dikembalikan oleh metode default hashCode (), apakah kelas objek yang diberikan mengganti hashCode () atau tidak. Kode hash untuk referensi null adalah nol.

Yang, untuk apa yang Anda lakukan, terdengar seperti yang Anda inginkan ... tetapi apa yang ingin Anda lakukan mungkin tidak aman tergantung pada bagaimana pustaka diimplementasikan.

TofuBeer
sumber
6
Saya tidak bertindak berdasarkan nilai dalam kode. Sebagai pr. pertanyaan saya edit, saya hanya menggunakannya untuk keperluan debugging dari situasi tertentu. Itulah mengapa saya merasa jawaban saya masuk akal, tetapi saya memberi Anda +1 untuk balasan yang berwawasan.
Nicolai
1
kemungkinan besar itu akan selalu melakukan apa yang Anda inginkan - tetapi dapat merusak beberapa VM.
TofuBeer
Ini akan rusak (yaitu identityHashCode tidak selalu unik) pada VM yang wajar. identityHashCode bukan ID
Tom Hawtin - tackline
Seperti yang telah disebutkan, tidak ada jaminan kode hash yang didasarkan pada alamat. Saya telah melihat beberapa objek dengan ID yang sama terjadi di VM IBM di dalam WAS.
Robin
"Ini biasanya diimplementasikan dengan mengubah alamat internal objek menjadi integer" bukan jaminan, tetapi implementasi default dari Sun. Hal-hal seperti s = "Hello" dan t = "Hello" mungkin akan menghasilkan s dan t yang memiliki identityHashCode yang sama karena keduanya merupakan objek yang sama.
TofuBeer
50

Beginilah cara saya menyelesaikannya:

Integer.toHexString(System.identityHashCode(object));
Nicolai
sumber
5
Ini sebenarnya tidak benar, karena beberapa objek dapat mengembalikan identityHashCode yang sama.
Robin
2
Bukankah benar bahwa dua objek (referensi) dengan hash identitas yang sama adalah objek yang sama? itulah yang diinginkan OP
basszero
3
Tidak, itu tidak benar. Sangat mungkin, tetapi tidak dijamin karena spesifikasi TIDAK menentukan algoritme.
Robin
8

Ganda sama dengan ==akan selalu memeriksa berdasarkan identitas objek, terlepas dari implementasi objek dari kode hash atau sama. Tentu saja - pastikan referensi objek yang Anda bandingkan volatile(dalam JVM 1,5+).

Jika Anda benar-benar harus memiliki hasil toString Object asli (meskipun itu bukan solusi terbaik untuk contoh kasus penggunaan Anda), perpustakaan Commons Lang memiliki metode ObjectUtils.identityToString (Object) yang akan melakukan apa yang Anda inginkan. Dari JavaDoc:

public static java.lang.String identityToString(java.lang.Object object)

Mendapatkan toString yang akan diproduksi oleh Object jika kelas tidak mengganti toString itu sendiri. null akan mengembalikan null.

 ObjectUtils.identityToString(null)         = null
 ObjectUtils.identityToString("")           = "java.lang.String@1e23"
 ObjectUtils.identityToString(Boolean.TRUE) = "java.lang.Boolean@7fa"
noahlz
sumber
1
Jika Anda menggunakan Java 7, maka Anda harus mempertimbangkan untuk menggunakan java.util.Objects
noahlz
5

Anda tidak dapat melakukan apa yang Anda inginkan dengan aman karena hashCode () default mungkin tidak mengembalikan alamat tersebut, dan telah disebutkan, beberapa objek dengan kode hash yang sama dimungkinkan. Satu-satunya cara untuk mencapai apa yang Anda inginkan adalah dengan benar-benar mengganti metode hashCode () untuk objek yang dimaksud dan menjamin bahwa semuanya memberikan nilai yang unik. Apakah ini layak dalam situasi Anda adalah pertanyaan lain.

Sebagai catatan, saya telah mengalami beberapa objek dengan kode hash default yang sama di VM IBM yang berjalan di server WAS. Kami memiliki cacat di mana objek yang dimasukkan ke cache jarak jauh akan ditimpa karena ini. Itu adalah pembuka mata bagi saya pada saat itu karena saya menganggap kode hash default adalah alamat memori objek juga.

Robin
sumber
2

Tambahkan id unik ke semua instance Anda, yaitu

public interface Idable {
  int id();
}

public class IdGenerator {
  private static int id = 0;
  public static synchronized int generate() { return id++; }
}

public abstract class AbstractSomething implements Idable {
  private int id;
  public AbstractSomething () {
    this.id = IdGenerator.generate();
  }
  public int id() { return id; }
}

Perluas dari AbstractSomething dan kueri properti ini. Akan aman di dalam satu vm (dengan asumsi tidak ada permainan yang dimainkan dengan classloader untuk menghindari statika).

Michael Myers
sumber
Saya mungkin akan menggunakan AtomicInteger dalam skenario ini - ia memiliki throughput yang lebih tinggi karena sinkronisasi tidak diperlukan dan menggunakan operasi memori atom asli yang disediakan olehsun.misc.Unsafe
RAnders00
1

kita cukup menyalin kode dari tostring kelas objek untuk mendapatkan referensi string

class Test
{
  public static void main(String args[])
  {
    String a="nikhil";     // it stores in String constant pool
    String s=new String("nikhil");    //with new stores in heap
    System.out.println(Integer.toHexString(System.identityHashCode(a)));
    System.out.println(Integer.toHexString(System.identityHashCode(s)));
  }
}
Nikhil Jaitak
sumber