Comment obtenir la «référence d'objet» d'un objet en java lorsque toString () et hashCode () ont été remplacés?

106

Je voudrais imprimer la "référence d'objet" d'un objet en Java à des fins de débogage. Ie pour s'assurer que l'objet est le même (ou différent) selon la situation.

Le problème est que la classe en question hérite d'une autre classe, qui a remplacé à la fois toString () et hashCode (), ce qui me donnerait généralement l'identifiant.

Exemple de situation: exécution d'une application multithread, où je veux (pendant le développement) vérifier si tous les threads utilisent la même instance d'un objet ressource ou non.

Nicolai
la source
1
selon si vous pouvez le faire du tout ... == est la voie à suivre ... mais je n'ai aucune idée de la structure du code en question. Encore une fois, hashCode convient probablement à ce que vous faites, mais il pourrait se casser en fonction de la façon dont la bibliothèque est implémentée.
TofuBeer
C'est vraiment une bonne question.
Zhang Xiang

Réponses:

108

Que comptez-vous faire exactement (ce que vous voulez faire fait une différence avec ce que vous devrez appeler).

hashCode, tel que défini dans les JavaDocs, dit:

Autant que cela soit raisonnablement pratique, la méthode hashCode définie par la classe Object renvoie des entiers distincts pour des objets distincts. (Ceci est généralement implémenté en convertissant l'adresse interne de l'objet en un entier, mais cette technique d'implémentation n'est pas requise par le langage de programmation Java ™.)

Donc, si vous utilisez hashCode()pour savoir s'il s'agit d'un objet unique en mémoire, ce n'est pas une bonne façon de le faire.

System.identityHashCode fait ce qui suit:

Renvoie le même code de hachage pour l'objet donné que celui qui serait renvoyé par la méthode par défaut hashCode (), que la classe de l'objet donné remplace ou non hashCode (). Le code de hachage pour la référence null est zéro.

Ce qui, pour ce que vous faites, ressemble à ce que vous voulez ... mais ce que vous voulez faire peut ne pas être sûr selon la façon dont la bibliothèque est implémentée.

TofuBière
la source
6
Je n'agis pas sur la valeur du code. Comme pr. ma question modifier, je ne l'utilise qu'à des fins de débogage d'une certaine situation. C'est pourquoi je pense que ma réponse est raisonnable, mais je vous donne un +1 pour une réponse perspicace.
Nicolai
1
il y a de fortes chances qu'il fasse toujours ce que vous voulez - mais cela pourrait casser sur certaines machines virtuelles.
TofuBeer
Il se cassera (ie identityHashCode ne sera pas nécessairement unique) sur n'importe quelle VM raisonnable. identityHashCode n'est pas un ID
Tom Hawtin - tackline
Comme cela a été mentionné, il n'y a aucune garantie que le hashcode soit basé sur l'adresse. J'ai vu plusieurs objets avec le même ID se produire dans la machine virtuelle IBM à l'intérieur de WAS.
Robin
"Ceci est généralement mis en œuvre en convertissant l'adresse interne de l'objet en un entier" non pas une garantie, mais l'implémentation par défaut de Sun. Des choses comme s = "Hello" et t = "Hello" aboutiraient probablement à ce que s et t aient le même identityHashCode car ils sont vraiment le même objet.
TofuBeer
50

Voici comment je l'ai résolu:

Integer.toHexString(System.identityHashCode(object));
Nicolai
la source
5
Ce n'est pas réellement correct, car plusieurs objets peuvent renvoyer le même identityHashCode.
Robin
2
N'est-il pas vrai que deux objets (références) avec le même hachage d'identité sont le même objet? c'est ce que veut OP
basszero
3
Non, ce n'est pas vrai. C'est très probable, mais pas garanti car la spécification ne définit PAS l'algorithme.
Robin
8

Double equals ==vérifiera toujours en fonction de l'identité de l'objet, quelle que soit l'implémentation des objets de hashCode ou equals. Bien sûr, assurez-vous que les références d'objet que vous comparez sont volatile(dans une JVM 1.5+).

Si vous devez vraiment avoir le résultat Object toString d'origine (bien que ce ne soit pas la meilleure solution pour votre exemple d'utilisation), la bibliothèque Commons Lang a une méthode ObjectUtils.identityToString (Object) qui fera ce que vous voulez. À partir du JavaDoc:

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

Obtient le toString qui serait produit par Object si une classe ne remplaçait pas toString lui-même. null renverra null.

 ObjectUtils.identityToString(null)         = null
 ObjectUtils.identityToString("")           = "java.lang.String@1e23"
 ObjectUtils.identityToString(Boolean.TRUE) = "java.lang.Boolean@7fa"
Noahlz
la source
1
Si vous utilisez Java 7, vous devriez envisager d'utiliser java.util.Objects
noahlz
5

Vous ne pouvez pas faire ce que vous voulez en toute sécurité car le hashCode () par défaut peut ne pas renvoyer l'adresse, et a été mentionné, plusieurs objets avec le même hashCode sont possibles. La seule façon d'accomplir ce que vous voulez est de remplacer la méthode hashCode () pour les objets en question et de garantir qu'ils fournissent tous des valeurs uniques. Si cela est faisable dans votre situation est une autre question.

Pour mémoire, j'ai rencontré plusieurs objets avec le même hashcode par défaut dans une machine virtuelle IBM s'exécutant sur un serveur WAS. Nous avions un défaut où les objets placés dans un cache distant étaient écrasés à cause de cela. Cela m'a ouvert les yeux à ce moment-là, car je supposais que le hashcode par défaut était également l'adresse mémoire des objets.

Robin
la source
2

Ajoutez un identifiant unique à toutes vos instances, c'est-à-dire

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

Extend de AbstractSomething et interroge cette propriété. Sera en sécurité dans une seule VM (en supposant qu'aucun jeu ne joue avec des chargeurs de classe pour contourner la statique).

Michael Myers
la source
J'utiliserais probablement AtomicInteger dans ce scénario - il a un débit plus élevé car la synchronisation n'est pas nécessaire et il utilise les opérations de mémoire atomique native fournies parsun.misc.Unsafe
RAnders00
1

on peut simplement copier le code de la chaîne de la classe d'objet pour obtenir la référence de la chaîne

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
la source