2 votes

Hashmap donne Null quand on essaie de récupérer un objet qui existe en elle

Je me heurte à ce problème étrange où j'itère sur une liste de réponses. Lorsque j'essaie d'obtenir la réponse de chaque réponse par la question, la plupart d'entre elles obtiennent la réponse correctement, sauf une où obtenir la réponse à partir du hashmap donne null. J'ai lancé le mode débogage dans Eclipse, et j'ai comparé la question dont j'essaie d'obtenir la valeur à partir du hashmap getAnswerMap() avec celle qui se trouve à l'intérieur de ce hashmap et les deux semblent être exactement les mêmes, mais j'obtiens toujours null.

for (SurveyResponse response : responses) {
      MultipleChoiceAnswer answer = (MultipleChoiceAnswer) response.getAnswerMap().get(question);
        ....
        ....
      }

Ensuite, j'ai pensé qu'il s'agissait d'un problème de code de hachage, j'ai donc ajouté une autre vilaine ligne de code pour vérifier les codes de hachage, et ils ont en fait le même code de hachage et la ligne supplémentaire suivante a fonctionné et a donné une réponse correcte.

for (SurveyResponse response : responses) {
      MultipleChoiceAnswer answer = (MultipleChoiceAnswer) response.getAnswerMap().get(question);
      for (Entry entry: response.getAnswerMap().entrySet()) {
        if (entry.getKey().hashCode() == question.hashCode()) answer = (MultipleChoiceAnswer) entry.getValue();
        ....
        ....
      }

Cependant, c'est très laid et j'aimerais vraiment obtenir la réponse correcte à partir du hashmap. Avez-vous des suggestions ?

MISE À JOUR : L'appel des méthodes hashCode() et equals() sur les deux objets montre que les deux ont des hashcodes égaux et que equals() renvoie vrai. Je soupçonne que, comme l'indique l'une des réponses ci-dessous, le problème pourrait être que la question a été insérée avec un hashcode différent lorsqu'elle a été insérée dans le hashmap. Par conséquent, l'appel de la méthode get en question renvoie null car l'objet que j'essaie d'obtenir n'a pas le même hashcode que l'ancien. Des réponses extrêmement utiles les gars !

3voto

MatrixFrog Points 11066

Une chose à laquelle il faut faire attention : Assurez-vous que la classe que vous utilisez comme clé est immuable -- Sinon, une clé correspondra à une chose lorsque vous l'insérerez, mais à une autre lorsque vous la retirerez.

Edit : It doesn't ont pour être immuable, mais il doit être vrai qu'il ne peut être modifié que d'une manière qui ne change pas le hashcode. Rendre l'objet entier immuable est la façon la plus simple de le faire, mais ce n'est pas la seule.

2voto

Paŭlo Ebermann Points 35526

Une autre supposition de boule de verre :

Vous avez une méthode d'égalisation comme celle-ci :

class Question {

    // ...

    public boolean equals(Question q) {
       // do intelligent comparison
    }

    public int hashCode() {
        // calculate hash code
    }

}

Mais ici vous ne remplacez pas vraiment le equals(Object) méthode de l'objet, mais il suffit d'en déclarer un nouveau à côté de celui-ci. Le HashMap ne sait rien de votre nouvelle méthode, il appellera simplement la méthode originale pour comparer votre objet clé dans la carte avec la clé de la requête (après avoir trouvé un objet avec le hashCode correspondant).

Déclarez la méthode comme ceci, à la place :

    @Override
    public boolean equals(Object o) {
        if(! (o instanceof Question))
           return false;
        Question q = (Question)o;
        // do intelligent comparison
    }

(Le @Override permet au compilateur de vérifier que vous surchargez réellement une méthode ici, et non pas que vous en créez une nouvelle).

1voto

Pour faire d'un objet une clé 100% déterministe avec un HashMap vous devez passer outre hashCode() y equals() où ils sont cohérents dans ce equals() retourne toujours true lorsque le hashCode() sont les mêmes.

Voici un vieux article de Brian Goetz sur IBM developerWorks, mais son contenu est toujours d'actualité :

Pourquoi surcharger equals() et hashCode() ?

Que se passerait-il si Integer n'a pas passer outre equals() y hashCode() ? Rien, si nous n'avons jamais utilisé un Integer comme une clé dans un HashMap ou autre collection basée sur le hachage. Cependant, si nous devions utiliser un tel objet Integer pour pour une clé dans un HashMap, nous ne pourrions pas capable de récupérer de manière fiable l'objet valeur associée, à moins que nous n'utilisions le exactement le même Integer dans le get() comme nous l'avons fait dans le put() appel. Pour ce faire, il faudrait s'assurer que nous n'utilisons qu'une seule instance de la Integer correspondant à un valeur entière particulière dans tout le notre programme. Il va sans dire que cette approche serait peu pratique et source d'erreurs.

Le contrat d'interface pour Object exige que si deux objets sont égaux selon equals() alors ils doivent avoir la même hashCode() valeur. Pourquoi notre classe d'objets Root a besoin de hashCode() lorsque son sa capacité de discrimination est entièrement subsumée par celle de equals() ? Le site hashCode() existe uniquement pour efficacité. La plate-forme Java ont anticipé l'importance des classes de collections basées sur le hachage -- telles que Hashtable , HashMap et HashSet -- dans le cas typique de Java et en comparant avec de nombreux objets avec equals() peut être coûteux en termes de calcul. Avoir chaque objet Java supporte hashCode() permet un stockage efficace et efficace en utilisant une des collections.

0voto

Bozho Points 273663

Il est probable que vous n'ayez pas surchargé equals(..) correctement - il s'agit d'une exigence pour un HashMap pour fonctionner correctement

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X