Làm cách nào để bạn có được “tham chiếu đối tượng” của một đối tượng trong java khi toString () và hashCode () đã bị ghi đè?


106

Tôi muốn in "tham chiếu đối tượng" của một đối tượng trong Java cho mục đích gỡ lỗi. Tức là để đảm bảo rằng đối tượng giống nhau (hoặc khác nhau) tùy thuộc vào tình huống.

Vấn đề là lớp được đề cập kế thừa từ một lớp khác, lớp này đã ghi đè cả toString () và hashCode () mà thường sẽ cung cấp cho tôi id.

Tình huống ví dụ: Đang chạy một ứng dụng đa luồng, trong đó tôi (trong quá trình phát triển) muốn kiểm tra xem tất cả các luồng có sử dụng cùng một thể hiện của một đối tượng tài nguyên hay không.


1
tùy thuộc vào nếu bạn có thể làm điều đó ở tất cả ... == là cách để đi ... nhưng tôi không biết mã được đề cập đến được cấu trúc như thế nào. Một lần nữa, mã băm có thể tốt cho những gì bạn đang làm, nhưng nó có thể bị hỏng tùy thuộc vào cách thư viện được triển khai.
TofuBeer

Đó thực sự là một câu hỏi hay.
Zhang Xiang

Câu trả lời:


108

Chính xác thì bạn đang định làm gì với nó (những gì bạn muốn làm sẽ tạo ra sự khác biệt với những gì bạn sẽ cần gọi).

hashCode, như được định nghĩa trong JavaDocs, cho biết:

Về mặt thực tế, phương thức hashCode được định nghĩa bởi class Object không trả về các số nguyên riêng biệt cho các đối tượng riêng biệt. (Điều này thường được thực hiện bằng cách chuyển đổi địa chỉ nội bộ của đối tượng thành một số nguyên, nhưng kỹ thuật triển khai này không được ngôn ngữ lập trình Java ™ yêu cầu.)

Vì vậy, nếu bạn đang sử dụng hashCode()để tìm hiểu xem nó có phải là một đối tượng duy nhất trong bộ nhớ hay không thì đó không phải là một cách hay.

System.identityHashCode làm như sau:

Trả về cùng một mã băm cho đối tượng đã cho giống như sẽ được trả về bởi phương thức mặc định hashCode (), cho dù lớp của đối tượng đã cho có ghi đè hashCode () hay không. Mã băm cho tham chiếu rỗng là 0.

Điều này, đối với những gì bạn đang làm, có vẻ giống như những gì bạn muốn ... nhưng những gì bạn muốn làm có thể không an toàn tùy thuộc vào cách thư viện được triển khai.


6
Tôi không hành động trên giá trị trong mã. Như pr. câu hỏi của tôi chỉnh sửa, tôi chỉ sử dụng nó cho mục đích gỡ lỗi của một tình huống nhất định. Đó là lý do tại sao tôi cảm thấy câu trả lời của mình là hợp lý, nhưng tôi cho bạn +1 cho câu trả lời sâu sắc.
Nicolai

1
tỷ lệ cược là nó sẽ luôn làm những gì bạn muốn - nhưng nó có thể bị hỏng trên một số máy ảo.
TofuBeer

Nó sẽ bị hỏng (tức là Mã nhận dạng không nhất thiết sẽ là duy nhất) trên bất kỳ máy ảo hợp lý nào. identityHashCode không phải là một ID
Tom Hawtin - tackline

Như đã được đề cập, không có người nhận biết mã băm dựa trên địa chỉ. Tôi đã thấy nhiều đối tượng có cùng ID xuất hiện trong máy ảo IBM bên trong WAS.
Robin

"Điều này thường được thực hiện bằng cách chuyển đổi địa chỉ nội bộ của đối tượng thành một số nguyên" không phải là một người bảo lãnh, mà là việc triển khai mặc định từ Sun. Những thứ như s = "Hello" và t = "Hello" có thể dẫn đến s và t có cùng một mã nhận dạng HashCode vì chúng thực sự là cùng một đối tượng.
TofuBeer 24/02/09

50

Đây là cách tôi đã giải quyết nó:

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

5
Điều này không thực sự chính xác, vì nhiều đối tượng có thể trả về cùng một Mã nhận dạng.
Robin

2
Có đúng là hai đối tượng (tham chiếu) có cùng một băm đồng nhất là cùng một đối tượng không? đó là những gì OP muốn
basszero

3
Không, nó không đúng. Nó rất có thể xảy ra, nhưng không được đảm bảo vì thông số kỹ thuật KHÔNG xác định thuật toán.
Robin

8

Double equals ==sẽ luôn kiểm tra dựa trên nhận dạng đối tượng, bất kể việc triển khai hashCode hay equals của đối tượng. Tất nhiên - hãy đảm bảo rằng các tham chiếu đối tượng mà bạn đang so sánh volatile(trong JVM 1,5+).

Nếu bạn thực sự phải có kết quả Object toString ban đầu (mặc dù nó không phải là giải pháp tốt nhất cho trường hợp sử dụng ví dụ của bạn), thư viện Commons Lang có một phương thức ObjectUtils.identityToString (Đối tượng) sẽ làm những gì bạn muốn. Từ JavaDoc:

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

Lấy toString sẽ được tạo ra bởi Object nếu một lớp không ghi đè lên chính nó. null sẽ trả về null.

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

1
Nếu bạn đang sử dụng Java 7, thì bạn nên xem xét sử dụng java.util.Objects
noahlz

5

Bạn không thể làm những gì bạn muốn một cách an toàn vì mã băm mặc định () có thể không trả về địa chỉ và đã được đề cập, có thể có nhiều đối tượng có cùng Mã băm. Cách duy nhất để đạt được điều bạn muốn là thực sự ghi đè phương thức hashCode () cho các đối tượng được đề cập và đảm bảo rằng tất cả chúng đều cung cấp các giá trị duy nhất. Liệu điều này có khả thi trong tình huống của bạn hay không là một câu hỏi khác.

Đối với bản ghi, tôi đã gặp phải nhiều đối tượng có cùng một mã băm mặc định trong máy ảo IBM chạy trong máy chủ WAS. Chúng tôi đã có một khiếm khuyết trong đó các đối tượng được đưa vào bộ đệm từ xa sẽ bị ghi đè vì điều này. Đó là một điều thú vị cho tôi vào thời điểm đó vì tôi giả định mã băm mặc định cũng là địa chỉ bộ nhớ đối tượng.


2

Thêm một id duy nhất vào tất cả các phiên bản của bạn, tức là

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

Mở rộng từ AbstractSomething và truy vấn thuộc tính này. Sẽ an toàn bên trong một vm duy nhất (giả sử không có trò chơi nào chơi với trình tải lớp để nhận được xung quanh tĩnh).


Tôi có thể sẽ sử dụng AtomicInteger trong trường hợp này - nó có thông lượng cao hơn vì không cần đồng bộ hóa và nó sử dụng các hoạt động bộ nhớ nguyên tử gốc được cung cấp bởisun.misc.Unsafe
RAnders00

1

chúng ta chỉ có thể sao chép mã từ tostring của lớp đối tượng để lấy tham chiếu của chuỗi

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)));
  }
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.