Come si ottiene il "riferimento all'oggetto" di un oggetto in java quando toString () e hashCode () sono stati sovrascritti?


106

Vorrei stampare il "riferimento all'oggetto" di un oggetto in Java per scopi di debug. Cioè per assicurarsi che l'oggetto sia lo stesso (o diverso) a seconda della situazione.

Il problema è che la classe in questione eredita da un'altra classe, che ha sovrascritto sia toString () che hashCode () che di solito mi darebbero l'id.

Situazione di esempio: esecuzione di un'applicazione multi-thread, in cui (durante lo sviluppo) desidero verificare se tutti i thread utilizzano la stessa istanza di un oggetto risorsa o meno.


1
a seconda che tu possa farlo del tutto ... == è la strada da percorrere ... ma non ho idea di come sia strutturato il codice in questione. Di nuovo hashCode probabilmente va bene per quello che stai facendo, ma potrebbe rompersi a seconda di come la libreria è implementata.
TofuBeer

È davvero una bella domanda.
Zhang Xiang

Risposte:


108

Che cosa hai intenzione di fare esattamente con esso (ciò che vuoi fare fa la differenza con ciò che dovrai chiamare).

hashCode, come definito in JavaDocs, dice:

Per quanto ragionevolmente pratico, il metodo hashCode definito dalla classe Object restituisce interi distinti per oggetti distinti. (Questo viene in genere implementato convertendo l'indirizzo interno dell'oggetto in un numero intero, ma questa tecnica di implementazione non è richiesta dal linguaggio di programmazione Java ™.)

Quindi, se stai usando hashCode()per scoprire se si tratta di un oggetto unico in memoria, non è un buon modo per farlo.

System.identityHashCode fa quanto segue:

Restituisce lo stesso codice hash per l'oggetto dato come sarebbe restituito dal metodo predefinito hashCode (), indipendentemente dal fatto che la classe dell'oggetto specificato sovrascriva hashCode (). Il codice hash per il riferimento null è zero.

Il che, per quello che stai facendo, suona come quello che vuoi ... ma quello che vuoi fare potrebbe non essere sicuro a seconda di come la libreria è implementata.


6
Non sto agendo sul valore nel codice. Come pr. modifica la mia domanda, lo uso solo per scopi di debug di una determinata situazione. Questo è il motivo per cui penso che la mia risposta sia ragionevole, ma ti do un +1 per una risposta perspicace.
Nicolai

1
le probabilità sono che farà sempre quello che vuoi, ma potrebbe non funzionare su alcune VM.
TofuBeer

Si interromperà (cioè identityHashCode non sarà necessariamente univoco) su qualsiasi VM ragionevole. identityHashCode non è un ID
Tom Hawtin - tackline

Come è stato accennato, non vi è alcuna garanzia che il codice hash si basi sull'indirizzo. Ho visto più oggetti con lo stesso ID nella VM IBM all'interno di WAS.
Robin

"Questo viene in genere implementato convertendo l'indirizzo interno dell'oggetto in un intero" non una garanzia, ma l'implementazione predefinita di Sun. Cose come s = "Hello" et = "Hello" probabilmente risulterebbero in s e t con lo stesso identityHashCode in quanto sono in realtà lo stesso oggetto.
TofuBeer

50

Ecco come l'ho risolto:

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

5
Ciò non è effettivamente corretto, poiché più oggetti possono restituire la stessa identitàHashCode.
Robin

2
Non è vero che due oggetti (riferimenti) con lo stesso hash di identità sono lo stesso oggetto? questo è quello che vuole OP
basszero

3
No, non è vero. È molto probabile, ma non garantito poiché le specifiche NON definiscono l'algoritmo.
Robin

8

Double equals ==verificherà sempre in base all'identità dell'oggetto, indipendentemente dall'implementazione di hashCode o uguale da parte degli oggetti. Ovviamente, assicurati che i riferimenti agli oggetti che stai confrontando siano volatile(in una JVM 1.5+).

Se davvero devi avere il risultato Object toString originale (sebbene non sia la soluzione migliore per il tuo caso d'uso di esempio), la libreria Commons Lang ha un metodo ObjectUtils.identityToString (Object) che farà quello che vuoi. Dal JavaDoc:

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

Ottiene la toString che verrebbe prodotta da Object se una classe non eseguisse l'override di toString stessa. null restituirà null.

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

1
Se stai usando Java 7, dovresti considerare l'utilizzo di java.util.Objects
noahlz

5

Non puoi fare ciò che vuoi in sicurezza poiché hashCode () predefinito potrebbe non restituire l'indirizzo ed è stato menzionato, sono possibili più oggetti con lo stesso hashCode. L'unico modo per ottenere ciò che desideri è effettivamente sovrascrivere il metodo hashCode () per gli oggetti in questione e garantire che tutti forniscano valori univoci. Se questo sia fattibile nella tua situazione è un'altra domanda.

Per la cronaca, ho riscontrato più oggetti con lo stesso codice hash predefinito in una VM IBM in esecuzione su un server WAS. Avevamo un difetto in cui gli oggetti inseriti in una cache remota venivano sovrascritti a causa di ciò. A quel punto mi ha aperto gli occhi, dato che presumevo che il codice hash predefinito fosse anche l'indirizzo di memoria degli oggetti.


2

Aggiungi un ID univoco a tutte le tue istanze, ad es

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

Estendi da AbstractSomething e interroga questa proprietà. Sarà al sicuro all'interno di una singola VM (supponendo che nessun gioco venga giocato con classloader per aggirare la statica).


Probabilmente userò AtomicInteger in questo scenario: ha un throughput più elevato perché la sincronizzazione non è richiesta e utilizza operazioni di memoria atomica native fornite dasun.misc.Unsafe
RAnders00

1

possiamo semplicemente copiare il codice dal tostring della classe oggetto per ottenere il riferimento della stringa

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)));
  }
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.