Forum |  HardWare.fr | News | Articles | PC | S'identifier | S'inscrire | Shop Recherche
2534 connectés 

  FORUM HardWare.fr
  Programmation
  Java

  problème de HashMap résolu, mais besoin d'explications, bug JAVA ?

 


 Mot :   Pseudo :  
 
Bas de page
Auteur Sujet :

problème de HashMap résolu, mais besoin d'explications, bug JAVA ?

n°1836972
crazywolf
Posté le 12-01-2009 à 16:53:44  profilanswer
 

Salut à tous
 
Le sujet vous paraît peut-être un peu bizarre, mais en fait je viens de galérer 2h sur un problème, je l'ai résolu mais je n'en comprends toujours pas la cause, et je le partage pour voir si vous comprenez et pouvez m'expliquer.  
 
je récupère dans une base 4 String (pour chaque enregistrement), je créé une hashmap avec pour chaque ligne de la base, l'une des 4 String comme cle, et comme valeur un tableau de 3 String contenant les 3 autres String du résultat. Et ensuite je parcours ma HashMap par ses clés, et récupère les valeurs, pour traitement.  
 
Mon problème : lorsque je récupère les valeurs, la valeur est la même pour toutes les clés, alors que ce n'est pas le cas ni dans la base, ni donc dans le résultat de ma requête SQL (vérifié par debug)
 
Donc voici le code original :
 

Code :
  1. Statement stmt = con.createStatement();
  2.  ResultSet rs = stmt.executeQuery("SELECT USER_ID, USER_NAME, USER_MAIL, USER_GROUP FROM ALERT_USER, USERS Where ALERT_ID = \'" + alertId + "\' and USER_ID = ID_USER" );
  3.  HashMap users = new HashMap();
  4. String[] usersTab = new String[3];
  5.  while (rs.next()){
  6.   usersTab[0] = rs.getString(2);
  7.   usersTab[1] = rs.getString(3);
  8.   usersTab[2] = rs.getString(4);
  9.   sisErrorLog.info("usersTab.length "+usersTab.length);
  10.   users.put(rs.getString(1), usersTab);
  11.   sisErrorLog.info("usertab 1 " + usersTab[1]);
  12.  }
  13.  rs.close();
  14.  stmt.close();
  15.  Set clesU = users.keySet();
  16.      Iterator itUsers = clesU.iterator();
  17.  while (itUsers.hasNext()){
  18.   Object cle = itUsers.next();
  19.   sisErrorLog.info("cle : " + cle.toString());
  20.   String[] user = (String[])users.get(cle);
  21.   sisErrorLog.info("user " + user[1]);
  22.   user = null;
  23.  }


 
Et la sortie correspondante :  
 

Code :
  1. usersTab.length 3
  2. usertab 1 user1@mail.com
  3. usersTab.length 3
  4. usertab 1 user2@mail.com
  5. cle : 00002
  6. user user2@mail.com
  7. cle : 00001
  8. user user2@mail.com


 
Et la résolution du problème :  
 

Code :
  1. Statement stmt = con.createStatement();
  2.  ResultSet rs = stmt.executeQuery("SELECT USER_ID, USER_NAME, USER_MAIL, USER_GROUP FROM ALERT_USER, USERS Where ALERT_ID = \'" + alertId + "\' and USER_ID = ID_USER" );
  3.  HashMap users = new HashMap();
  4.  while (rs.next()){
  5.   String[] usersTab = new String[3];
  6.   usersTab[0] = rs.getString(2);
  7.   usersTab[1] = rs.getString(3);
  8.   usersTab[2] = rs.getString(4);
  9.   sisErrorLog.info("usersTab.length "+usersTab.length);
  10.   users.put(rs.getString(1), usersTab);
  11.   sisErrorLog.info("usertab 1 " + usersTab[1]);
  12.  }
  13.  rs.close();
  14.  stmt.close();
  15.  Set clesU = users.keySet();
  16.      Iterator itUsers = clesU.iterator();
  17.  while (itUsers.hasNext()){
  18.   Object cle = itUsers.next();
  19.   sisErrorLog.info("cle : " + cle.toString());
  20.   String[] user = (String[])users.get(cle);
  21.   sisErrorLog.info("user " + user[1]);
  22.   user = null;
  23.  }


 
 
Donc en déplaçant la création de mon tableau dans la boucle, ça résout le problème et je récupère bien des valeurs différentes pour chaque clé. Ce que vous voyez ici est bien sûr le code qui m'a permis de faire le debug, et non le code final, ma HashMap ne sera pas parcourue localement au final, sinon pas d'intérêt. Je suis passé par pas mal d'étapes, entre autres une foule de logs à toutes les étapes du code, mais tout semblait normal, sauf la valeur que je récupérais (user2@mail.com). Merci à ceux qui essaieront de comprendre et de m'expliquer !
 
EDIT : java 1.5 utilisé


Message édité par crazywolf le 12-01-2009 à 17:34:33
mood
Publicité
Posté le 12-01-2009 à 16:53:44  profilanswer
 

n°1837006
Bidem
Posté le 12-01-2009 à 17:48:30  profilanswer
 

Code :
  1. String[] usersTab = new String[3];
  2. while (rs.next()){
  3.   usersTab[0] = rs.getString(2);
  4.   usersTab[1] = rs.getString(3);
  5.   usersTab[2] = rs.getString(4);
  6.   ...
  7.   users.put(rs.getString(1), usersTab);
  8.   ...
  9. }


A chaque fois tu mets la même instance de tableau dans ta Map donc au final, tu as une Map avec 3 clées mais chaque clée retourne le même tableau.
 
avec le code ci-dessous, tu verras mieux ce qu'il y a vraiment dans ta Map

Code :
  1. Set clesU = users.keySet();
  2. Iterator itUsers = clesU.iterator();
  3. while (itUsers.hasNext()){
  4.   Object cle = itUsers.next();
  5.   String[] valeur = (String[])users.get(cle);
  6.   sisErrorLog.info(cle + " : " + valeur.toString()); // affichage de l'adresse en mémoire du tableau
  7. }


 
Normalement avec l'ancien code tu verras tes 3 clés suivies de 3 fois la même adresse

Message cité 1 fois
Message édité par Bidem le 12-01-2009 à 17:50:03
n°1837016
crazywolf
Posté le 12-01-2009 à 18:04:28  profilanswer
 

Effectivement j'utilise la même instance de tableau a chaque fois dans ma boucle, mais je réaffecte toute les valeurs du tableau à chaque passage de la boucle. Et j'ai vérifié, mes rs.getString ne sont jamais vides ou null. Et de plus, on voit bien dans mon exemple que la valeur de la 2e entrée du tableau n'est pas la même au 2e passage.

 

Mais je viens de faire un test, si je fais la même chose avec une String au lieu d'un tableau de String, je n'ai pas de soucis. Donc pourquoi dans le cas d'un tableau de String c'est l'adresse de mon tableau qui est passée, et pas dans le cas d'une simple String ?

 

[EDIT] : Et merci pour la réponse

 
Bidem a écrit :

Code :
  1. String[] usersTab = new String[3];
  2. while (rs.next()){
  3.   usersTab[0] = rs.getString(2);
  4.   usersTab[1] = rs.getString(3);
  5.   usersTab[2] = rs.getString(4);
  6.   ...
  7.   users.put(rs.getString(1), usersTab);
  8.   ...
  9. }


A chaque fois tu mets la même instance de tableau dans ta Map donc au final, tu as une Map avec 3 clées mais chaque clée retourne le même tableau.

 

avec le code ci-dessous, tu verras mieux ce qu'il y a vraiment dans ta Map

Code :
  1. Set clesU = users.keySet();
  2. Iterator itUsers = clesU.iterator();
  3. while (itUsers.hasNext()){
  4.   Object cle = itUsers.next();
  5.   String[] valeur = (String[])users.get(cle);
  6.   sisErrorLog.info(cle + " : " + valeur.toString()); // affichage de l'adresse en mémoire du tableau
  7. }
 

Normalement avec l'ancien code tu verras tes 3 clés suivies de 3 fois la même adresse


Message cité 1 fois
Message édité par crazywolf le 12-01-2009 à 18:07:53
n°1837054
el muchach​o
Comfortably Numb
Posté le 12-01-2009 à 19:20:53  profilanswer
 

crazywolf a écrit :


Mais je viens de faire un test, si je fais la même chose avec une String au lieu d'un tableau de String, je n'ai pas de soucis. Donc pourquoi dans le cas d'un tableau de String c'est l'adresse de mon tableau qui est passée, et pas dans le cas d'une simple String ?


Ben si, c'est pareil. A chaque fois, il faut allouer l'objet qui va contenir les nouvelles données (le premier élément d'un tableau ou une String) et la Hashmap retourne l'adresse de cet objet. Dans ton premier code, tu n'alloues pas d'emplacement mémoire, tu écrases l'existant, donc à tout moment, tu n'as que le drenier enregistrement dans le tableau et toutes tes valeurs de Hashmap pointent dessus.

Message cité 1 fois
Message édité par el muchacho le 12-01-2009 à 19:24:38

---------------
Les aéroports où il fait bon attendre, voila un topic qu'il est bien
n°1837138
Taz
bisounours-codeur
Posté le 12-01-2009 à 23:45:48  profilanswer
 

Il est où ton site web qu'on vienne y faire de l'injection ?

n°1837251
crazywolf
Posté le 13-01-2009 à 11:51:33  profilanswer
 

Taz a écrit :

Il est où ton site web qu'on vienne y faire de l'injection ?


 
C'est pas un site web, et les données ne viennent pas d'utilisateurs. De plus c'est la version debug d'un code qui me posait problème. Mais j'apprécie grandement ton effort pour m'aider, et bien sûr ton jugement éclairé sur mon code...

n°1837254
Taz
bisounours-codeur
Posté le 13-01-2009 à 11:54:09  profilanswer
 

Bah autant pas saper ton travail dès le début en adoptant des techniques adéquates.

n°1837255
crazywolf
Posté le 13-01-2009 à 11:56:14  profilanswer
 

el muchacho a écrit :


Ben si, c'est pareil. A chaque fois, il faut allouer l'objet qui va contenir les nouvelles données (le premier élément d'un tableau ou une String) et la Hashmap retourne l'adresse de cet objet. Dans ton premier code, tu n'alloues pas d'emplacement mémoire, tu écrases l'existant, donc à tout moment, tu n'as que le drenier enregistrement dans le tableau et toutes tes valeurs de Hashmap pointent dessus.


 
Bah apparemment non, je viens de refaire le test pour être sûr, avec ce code :
 

Code :
  1. String result = "";
  2.  while (rs.next()){
  3.   String[] usersTab = new String[3];
  4.   usersTab[0] = rs.getString(2);
  5.   usersTab[1] = rs.getString(3);
  6.   usersTab[2] = rs.getString(4);
  7.   result = usersTab[1];
  8.   users.put(rs.getString(1), result);
  9.   sisErrorLog.info("usertab 1 " + usersTab[1]);
  10.  }


 
Et lorsque je récupère le résultat la String récupérée est différente pour chaque clé. Mais du coup je me pose la question : quel est le comportement normal ? Et surtout comment savoir le comportement de la HashMap pour les autres types ?

n°1837258
crazywolf
Posté le 13-01-2009 à 12:00:59  profilanswer
 

Taz a écrit :

Bah autant pas saper ton travail dès le début en adoptant des techniques adéquates.


 
Bah en réfléchissant justement je me suis rendu compte que ta remarque était en plus complètement inutile. Dans mon code il n'y a qu'une donnée qui n'est pas récupérée de la base, et rien ne t'indique que je n'ai déjà traitée cette donnée. Donc pour l'injection tu repasseras...

n°1837291
Taz
bisounours-codeur
Posté le 13-01-2009 à 13:31:32  profilanswer
 

Bon c'est pas grave, continue avec tes mauvaises pratiques.

mood
Publicité
Posté le 13-01-2009 à 13:31:32  profilanswer
 

n°1837306
crazywolf
Posté le 13-01-2009 à 14:15:30  profilanswer
 

Taz a écrit :

Bon c'est pas grave, continue avec tes mauvaises pratiques.


 
Mais oui, et toi continue à faire de la sécurité dans des formulaires web, chacun son truc !!

n°1837381
Bidem
Posté le 13-01-2009 à 15:23:04  profilanswer
 

Citation :

Effectivement j'utilise la même instance de tableau a chaque fois dans ma boucle, mais je réaffecte toute les valeurs du tableau à chaque passage de la boucle.


 
Dans ta Map tu n'as donc qu'un seul tableau qui contient les valeurs de ton dernier enregistrement (car quand tu réaffectes ses valeurs, ça écrase les anciennes) CQFD

n°1837385
crazywolf
Posté le 13-01-2009 à 15:29:35  profilanswer
 

Bidem a écrit :

Citation :

Effectivement j'utilise la même instance de tableau a chaque fois dans ma boucle, mais je réaffecte toute les valeurs du tableau à chaque passage de la boucle.


 
Dans ta Map tu n'as donc qu'un seul tableau qui contient les valeurs de ton dernier enregistrement (car quand tu réaffectes ses valeurs, ça écrase les anciennes) CQFD


 
Oui mais ça n'explique pas mon dernier test. Quand je réaffecte la valeur de ma String qui est déclarée en dehors de ma boucle (comme l'était mon tableau), ça écrase bien aussi l'ancienne valeur. Et pourtant, au final dans ma HashMap je retrouve toutes les valeurs que j'ai ajoutées affectées successivement à  la String. Donc d'ou vient cette différence entre le tableau de String et la simple String ?


Message édité par crazywolf le 13-01-2009 à 15:35:20
n°1837528
Bidem
Posté le 13-01-2009 à 18:09:00  profilanswer
 

Citation :

Quand je réaffecte la valeur de ma String qui est déclarée en dehors de ma boucle (comme l'était mon tableau)


Le problème était surtout que ton tableau était instancié à l'extérieur. ça n'a rien à voir avec la déclaration
 
D'ailleurs voila un code où tout est déclaré à l'extérieur

Code :
  1. String[] usersTab;
  2. while (rs.next()){
  3.   userTab = new String[3]; // le mot clé important ici est "new"
  4.   usersTab[0] = rs.getString(2);
  5.   usersTab[1] = rs.getString(3);
  6.   usersTab[2] = rs.getString(4);
  7.   ...
  8.   users.put(rs.getString(1), usersTab);
  9.   ...
  10. }


 
Oublie ton test avec juste la String ce n'est pas pertinent.
 

n°1837535
crazywolf
Posté le 13-01-2009 à 18:17:18  profilanswer
 

[quote]
 
Oublie ton test avec juste la String ce n'est pas pertinent.
 
[/quotemsg]
 
Je veux bien, mais j'aimerai quand même savoir pourquoi ça n'est pas pertinent, parce que si je te reprends, dans mon test avec la String, j'instancie aussi la String à l'extérieur lorsque je fais

Code :
  1. String result = "";

. Donc qu'est-ce qui fait que ce n'est pas pertinent ? Parce que c'est une primitive ?

n°1837596
el muchach​o
Comfortably Numb
Posté le 13-01-2009 à 21:53:33  profilanswer
 

String n'est aucunement une primitive, c'est un objet immutable. Quand tu réaffectes result, tu ne fais qu'affecter un pointeur sur l'adresse d'un nouvel objet String (et l'ancien sera balayé au prochain passage du garbage collector).

Message cité 1 fois
Message édité par el muchacho le 13-01-2009 à 21:58:18

---------------
Les aéroports où il fait bon attendre, voila un topic qu'il est bien
n°1837762
crazywolf
Posté le 14-01-2009 à 12:08:29  profilanswer
 

el muchacho a écrit :

String n'est aucunement une primitive, c'est un objet immutable. Quand tu réaffectes result, tu ne fais qu'affecter un pointeur sur l'adresse d'un nouvel objet String (et l'ancien sera balayé au prochain passage du garbage collector).

 

Haaaaa merci beaucoup !!! Enfin une réponse claire ;) J'avais vu ce concept d'immutable en faisant mes recherches, mais je comprenais pas exactement ce que ça impliquait. Je comprends encore mieux aussi pourquoi parfois le StringBuffer est bien plus performant dans certains cas, à moins que je sois encore à coté de la plaque. Par contre le garbage collector va-t-il vraiment virer l'ancienne valeur de la String si entre-temps une référence à été insérée dans la HashMap ? Il me semble que le garbage collector ne vire que les objets qui n'ont aucun pointeur qui les référencent non ?


Message édité par crazywolf le 14-01-2009 à 12:10:17
n°1837970
el muchach​o
Comfortably Numb
Posté le 14-01-2009 à 18:36:37  profilanswer
 

oui, avec l'opérateur + sur String, tu recopies les données dans une nouvelle String (l'allocation est rapide, pas la recopie).
Si la référence d'une String est insérée dans la Hashmap, le GC ne fera rien, mais dans ton exemple, la référence insérée dans la Hashmap change.

 

Ah, et au passage, si tu as l'intention de faire bcp de JDBC, je te conseille fortement de jeter un oeil à iBatis, qui simplifie fortement le boulot.


Message édité par el muchacho le 14-01-2009 à 18:45:15

---------------
Les aéroports où il fait bon attendre, voila un topic qu'il est bien

Aller à :
Ajouter une réponse
  FORUM HardWare.fr
  Programmation
  Java

  problème de HashMap résolu, mais besoin d'explications, bug JAVA ?

 

Sujets relatifs
probleme afficher imagearbre n-aire, probleme de code
besoin d'aide pour ecrire un programme en cprobleme php exec avec argument et espace
Gestion du clavier...Problème :(Perl et cgi: problème de split
Probleme Ecriture base de registre VB[Résolu] Récupération de mails en Imap et sauts de ligne
Besoin d'Aidefichier texte ouvert avec fgetcsv() en php (résolu)
Plus de sujets relatifs à : problème de HashMap résolu, mais besoin d'explications, bug JAVA ?


Copyright © 1997-2022 Hardware.fr SARL (Signaler un contenu illicite / Données personnelles) / Groupe LDLC / Shop HFR