คุณจะได้รับ "การอ้างอิงวัตถุ" ของวัตถุใน java ได้อย่างไรเมื่อ toString () และ hashCode () ถูกแทนที่


108

ฉันต้องการพิมพ์ "การอ้างอิงวัตถุ" ของวัตถุใน Java เพื่อวัตถุประสงค์ในการดีบัก คือตรวจสอบให้แน่ใจว่าวัตถุนั้นเหมือนกัน (หรือต่างกัน) ขึ้นอยู่กับสถานการณ์

ปัญหาคือคลาสที่เป็นปัญหาสืบทอดมาจากคลาสอื่นซึ่งได้ลบล้างทั้ง toString () และ hashCode () ซึ่งโดยปกติจะให้ id แก่ฉัน

สถานการณ์ตัวอย่าง: การรันแอ็พพลิเคชันแบบมัลติเธรดโดยที่ฉัน (ระหว่างการพัฒนา) ต้องการตรวจสอบว่าเธรดทั้งหมดใช้อินสแตนซ์ของออบเจ็กต์ทรัพยากรเดียวกันหรือไม่


1
ขึ้นอยู่กับว่าคุณสามารถทำได้หรือไม่ ... == เป็นวิธีที่จะไป ... แต่ฉันไม่รู้ว่ารหัสที่เป็นปัญหานั้นมีโครงสร้างอย่างไร อีกครั้ง hashCode นั้นใช้ได้ดีสำหรับสิ่งที่คุณกำลังทำอยู่ แต่อาจพังได้ขึ้นอยู่กับวิธีการใช้งานไลบรารี
TofuBeer

เป็นคำถามที่ดีจริงๆ
Zhang Xiang

คำตอบ:


109

คุณกำลังวางแผนจะทำอะไรกันแน่ (สิ่งที่คุณต้องการจะสร้างความแตกต่างกับสิ่งที่คุณจะต้องโทร)

hashCodeตามที่กำหนดไว้ใน JavaDocs กล่าวว่า:

เท่าที่ใช้งานได้จริงวิธี hashCode ที่กำหนดโดยคลาส Object จะส่งคืนจำนวนเต็มที่แตกต่างกันสำหรับอ็อบเจ็กต์ที่แตกต่างกัน (โดยทั่วไปจะดำเนินการโดยการแปลงที่อยู่ภายในของวัตถุเป็นจำนวนเต็ม แต่ภาษาโปรแกรม Java ™ไม่จำเป็นต้องใช้เทคนิคการใช้งานนี้)

ดังนั้นหากคุณกำลังใช้hashCode()เพื่อดูว่ามันเป็นวัตถุเฉพาะในหน่วยความจำซึ่งไม่ใช่วิธีที่ดีที่จะทำ

System.identityHashCode ทำสิ่งต่อไปนี้:

ส่งคืนรหัสแฮชเดียวกันสำหรับอ็อบเจ็กต์ที่กำหนดซึ่งจะถูกส่งกลับโดยเมธอดเริ่มต้น hashCode () ไม่ว่าคลาสของอ็อบเจ็กต์ที่ระบุจะแทนที่ hashCode () หรือไม่ รหัสแฮชสำหรับการอ้างอิง null คือศูนย์

ซึ่งสำหรับสิ่งที่คุณกำลังทำดูเหมือนว่าคุณต้องการ ... แต่สิ่งที่คุณต้องการทำอาจไม่ปลอดภัยขึ้นอยู่กับวิธีการใช้งานไลบรารี


6
ฉันไม่ได้ดำเนินการกับค่าในรหัส ในฐานะ pr. แก้ไขคำถามของฉันฉันใช้เพื่อจุดประสงค์ในการแก้ไขข้อบกพร่องของสถานการณ์บางสถานการณ์ นั่นคือเหตุผลที่ฉันรู้สึกว่าคำตอบของฉันมีเหตุผล แต่ฉันให้ +1 สำหรับคำตอบที่ชาญฉลาด
Nicolai

1
มันจะทำในสิ่งที่คุณต้องการเสมอ - แต่มันอาจจะพังใน VM บางตัว
TofuBeer

มันจะแตก (เช่น identityHashCode ไม่จำเป็นต้องซ้ำกัน) บน VM ที่สมเหตุสมผล identityHashCode ไม่ใช่ ID
Tom Hawtin - แทคไลน์

ดังที่ได้กล่าวไปแล้วไม่มีการรับประกันว่าแฮชโค้ดจะขึ้นอยู่กับที่อยู่ ฉันเห็นหลายอ็อบเจ็กต์ที่มี ID เดียวกันเกิดขึ้นใน IBM VM ภายใน WAS
Robin

"โดยทั่วไปจะดำเนินการโดยการแปลงที่อยู่ภายในของวัตถุเป็นจำนวนเต็ม" ไม่ใช่การรับประกัน แต่เป็นการใช้งานเริ่มต้นจาก Sun สิ่งต่างๆเช่น s = "Hello" และ t = "Hello" อาจส่งผลให้ s และ t มี identityHashCode เหมือนกันเนื่องจากเป็นวัตถุเดียวกัน
TofuBeer

51

นี่คือวิธีที่ฉันแก้ไข:

Integer.toHexString(System.identityHashCode(object));

5
สิ่งนี้ไม่ถูกต้องจริง ๆ เนื่องจากวัตถุหลายชิ้นสามารถส่งคืน identityHashCode เดียวกันได้
Robin

2
ไม่เป็นความจริงที่ว่าสองวัตถุ (การอ้างอิง) ที่มีเอกลักษณ์เดียวกันแฮชเป็นวัตถุเดียวกันหรือไม่? นั่นคือสิ่งที่ OP ต้องการ
basszero

3
ไม่มันไม่เป็นความจริง เป็นไปได้มาก แต่ไม่รับประกันเนื่องจากข้อมูลจำเพาะไม่ได้กำหนดอัลกอริทึม
Robin

8

ค่าเท่ากับสองเท่า==จะตรวจสอบตามเอกลักษณ์ของอ็อบเจ็กต์เสมอไม่ว่าอ็อบเจ็กต์จะใช้ hashCode หรือเท่ากับ แน่นอน - ตรวจสอบให้แน่ใจว่าการอ้างอิงออบเจ็กต์ที่คุณกำลังเปรียบเทียบเป็นvolatile(ใน 1.5+ JVM)

หากคุณต้องมีผลลัพธ์ Object toString ดั้งเดิมจริงๆ (แม้ว่าจะไม่ใช่ทางออกที่ดีที่สุดสำหรับตัวอย่างการใช้งานของคุณ) ไลบรารี Commons Lang มีเมธอดObjectUtils.identityToString (Object)ที่จะทำสิ่งที่คุณต้องการ จาก JavaDoc:

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

รับ toString ที่จะสร้างโดย Object หากคลาสไม่ได้แทนที่ toString เอง null จะคืนค่า null

 ObjectUtils.identityToString(null)         = null
 ObjectUtils.identityToString("")           = "java.lang.String@1e23"
 ObjectUtils.identityToString(Boolean.TRUE) = "java.lang.Boolean@7fa"

1
หากคุณใช้ Java 7 คุณควรพิจารณาใช้java.util.Objects
noahlz

5

คุณไม่สามารถทำสิ่งที่คุณต้องการได้อย่างปลอดภัยเนื่องจาก hashCode () เริ่มต้นอาจไม่ส่งคืนที่อยู่และมีการกล่าวถึงวัตถุหลายชิ้นที่มี hashCode เดียวกันเป็นไปได้ วิธีเดียวที่จะบรรลุสิ่งที่คุณต้องการคือการแทนที่เมธอด hashCode () สำหรับอ็อบเจ็กต์ที่เป็นปัญหาและรับประกันว่าทั้งหมดให้ค่าที่ไม่ซ้ำกัน เป็นไปได้หรือไม่ในสถานการณ์ของคุณเป็นคำถามอื่น

สำหรับเร็กคอร์ดฉันเคยพบอ็อบเจ็กต์หลายตัวที่มีแฮชโค้ดเริ่มต้นเดียวกันใน IBM VM ที่รันในเซิร์ฟเวอร์ WAS เรามีข้อบกพร่องที่วัตถุที่ถูกใส่ลงในแคชระยะไกลจะถูกเขียนทับเนื่องจากสิ่งนี้ นั่นเป็นเครื่องเปิดตาสำหรับฉัน ณ จุดนั้นเนื่องจากฉันคิดว่าแฮชโค้ดเริ่มต้นคือที่อยู่หน่วยความจำของวัตถุเช่นกัน


2

เพิ่มรหัสเฉพาะให้กับอินสแตนซ์ทั้งหมดของคุณเช่น

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

ขยายจาก AbstractSomething และสอบถามคุณสมบัตินี้ จะปลอดภัยภายใน vm เดียว (สมมติว่าไม่มีเกมที่เล่นกับ classloaders เพื่อหลีกเลี่ยงสถิต)


ฉันอาจจะใช้ AtomicInteger ในสถานการณ์นี้ - มันมีทsun.misc.Unsafe
รูพุตที่

1

เราสามารถคัดลอกโค้ดจากสตริงของคลาสอ็อบเจ็กต์เพื่อรับการอ้างอิงของสตริง

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)));
  }
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.