IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

avec Java Discussion :

Méthode equals() et les hashSet


Sujet :

avec Java

  1. #1
    Nouveau membre du Club
    Profil pro
    Inscrit en
    Décembre 2010
    Messages
    20
    Détails du profil
    Informations personnelles :
    Âge : 38
    Localisation : France, Nord (Nord Pas de Calais)

    Informations forums :
    Inscription : Décembre 2010
    Messages : 20
    Points : 26
    Points
    26
    Par défaut Méthode equals() et les hashSet
    Bonjour,

    Je suis autodidacte en Java et bien que j'ai résolu mon problème à l'aide de la rubrique suivante : http://java.developpez.com/faq/java/...#DIVERS_equals, je n'ai toujours pas compris pourquoi mon ancien code ne fonctionnait pas.

    Je dispose d'une classe Tag possédant entre autre un champ String Name (c'est le seul important ici). Je souhaite pouvoir utiliser les ensembles hashSet<Tag> pour la rapidité des méthodes add, remove, et contains, et surtout pour l'élimination automatique des doublons.
    J'ai donc redéfinit les méthodes hashCode et equals comme celà :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
     
    public class Tag
    {
    	private String name;
            private SomeType lotsOfOtherStuff;
     
           public Tag(String name) //constructeur
           {
                 this.name = name;
                 this.lotsOfOtherStuff = WhatItNeedToBe;
           }
     
           public getName()
           {return this.name;}
     
          public hashCode()
          {return this.name.hashCode();}
     
          public equals(Tag t)
          {
               return this.name.equals(t.getName());
          }
    }
    Et voici mon main dans lequelle je réalise mes tests :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
     
    public class TestProto
    {
    	public static void main(String[] args)
    	{
    		// test des hashSet<Tag> :
    		System.out.println("Etape de test n°0 : teste des HashSet de Tags :");
    		HashSet<Tag> listeTag = new HashSet<Tag>();
    		Tag T1 = new Tag("test");
    		Tag T2 = new Tag("test");
    		System.out.println("T1.equals(T2) ? : " + T1.equals(T2) + " =?= VRAI");
     
    		System.out.println("T1.hashCode() == T2.hashCode() ? :" + (T1.hashCode() == T2.hashCode()) + " =?= VRAI");
     
    		listeTag.add(T1);
    		System.out.println("listeTag.add(T2) ? : " + listeTag.add(T2) + " =?= FAUX");

    Voici ce que j'obtiens lors de l'éxécution :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    Etape de test n°0 : teste des HashSet de Tags :
    T1 == T2 ? : true =?= VRAI                                         <-- la méthode equals renvoie le bon résultat
    T1.hashCode() == T2.hashCode() ? :true =?= VRAI     <-- la méthode hashCode fonctionne comme prévu
    listeTag.add(T2) ? : true =?= FAUX                             <-- pourtant T2 est quand même rajouter à mon hashSet...

    La correction de la méthode equals à l'aide du lien plus haut résoud mon problème, mais çà ne me dit pas pourquoi avant çà ne marchait pas. Auriez-vous des explications svp ?

    Voici à titre indicatif ma nouvelle méthode equals :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
     
    	public boolean equals(Object T)
    	{	// Vérificationde la référence
    		if (T==this)
    			return true;
     
    		//Vérification du type du paramètre puis de ses attributs.
    		if (T instanceof Tag)
    		{
    			Tag Tbis = (Tag) T;
    			return this.name.equals(Tbis.getName());
     
    		} else
    			return false;
    	}

  2. #2
    Expert éminent sénior
    Avatar de tchize_
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Avril 2007
    Messages
    25 481
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    Localisation : Belgique

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Avril 2007
    Messages : 25 481
    Points : 48 806
    Points
    48 806
    Par défaut
    parce que, en utilisant le premier cas, ton objet à deux méthodes equals, avec des signature différente.

    equals(Tag) (que tu as créé)
    et
    equals(Object) (dont tu hérite).


    Et c'est toujoursl a deuxième qu'il faut redéfinir, la première n'étant qu'une méthode à toi que seul Tag utilise.

    Pour t'en convaincre, rajoute dans ton test le code suivant
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    		System.out.println("T1.equals((Object)T2) ? : " + T1.equals((Object)T2) + " =?= VRAI");

  3. #3
    Nouveau membre du Club
    Profil pro
    Inscrit en
    Décembre 2010
    Messages
    20
    Détails du profil
    Informations personnelles :
    Âge : 38
    Localisation : France, Nord (Nord Pas de Calais)

    Informations forums :
    Inscription : Décembre 2010
    Messages : 20
    Points : 26
    Points
    26
    Par défaut
    C'est justement ce que je ne comprends pas :

    equals(Tag) est plus spécifique que equals(Object) puisqu'en Java tous les objets dérive de la classe Object.

    Comme T1 et T2 sont tous deux de type Tag, la méthode plus spécifique equals(Tag) convient et devrait être appelé avant la méthode equals(Object), non ?

  4. #4
    Membre chevronné
    Inscrit en
    Août 2009
    Messages
    1 073
    Détails du profil
    Informations forums :
    Inscription : Août 2009
    Messages : 1 073
    Points : 1 806
    Points
    1 806
    Par défaut
    La méthode est appelée dans une autre classe générique. Celle ci n'a aucune raison de connaitre le type de l'objet (le type générique est effacé à la compilation) ; en conséquence elle ne peut pas savoir qu'il faut appeler equals(Tag) ; pour elle, tout se passe avec equals(Object).

  5. #5
    Nouveau membre du Club
    Profil pro
    Inscrit en
    Décembre 2010
    Messages
    20
    Détails du profil
    Informations personnelles :
    Âge : 38
    Localisation : France, Nord (Nord Pas de Calais)

    Informations forums :
    Inscription : Décembre 2010
    Messages : 20
    Points : 26
    Points
    26
    Par défaut
    Citation Envoyé par Rei Ichido Voir le message
    (le type générique est effacé à la compilation) ; en conséquence elle ne peut pas savoir qu'il faut appeler equals(Tag) ; pour elle, tout se passe avec equals(Object).
    Super merci ! J'ai appris quelque chose.
    Peut-on espérer un changement de ce comportement lors d'une futur mise à jour de Java ?

  6. #6
    Expert éminent sénior
    Avatar de tchize_
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Avril 2007
    Messages
    25 481
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    Localisation : Belgique

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Avril 2007
    Messages : 25 481
    Points : 48 806
    Points
    48 806
    Par défaut
    non, le typage des méthode (pour rechercher la méthode la plus spécifique) se fait par le compilateur, pas par la jvm (java n'est pas interprété). la méthode equals(Objet) n'est pas la méthode equals(Tag), aucune ne cache l'autre, ce sont des méthodes séparées! D'ailleurs si vous mettez @override sur votre méthode, vous contaterez que le compilateur vous dit qu'elle ne surcharge aucune méthode existante.

    Vouloir un changement là dessus, c'est vouloir qu'on change toutes les règles du java! Et ca n'a de toutes façons aucun sens.

  7. #7
    Nouveau membre du Club
    Profil pro
    Inscrit en
    Décembre 2010
    Messages
    20
    Détails du profil
    Informations personnelles :
    Âge : 38
    Localisation : France, Nord (Nord Pas de Calais)

    Informations forums :
    Inscription : Décembre 2010
    Messages : 20
    Points : 26
    Points
    26
    Par défaut
    @tchize_ : tu soulignes la différence entre la redéfinition (en anglais override) et la surdéfinition (si qqun connait le terme anglais je suis preneur).
    La redéfinition nécessite de reprendre exactement la même signature.
    La surdéfinition permet (dans ce cas-ci) de préciser la méthode equals en l'étendant à d'autre classe que la classe Object.
    Dans mon exemple, j'étends la définition à une classe dérivée de la classe Object : la classe Tag. Donc dans mon main, lorsque je fais l'appel T1.equals(T2), le compilateur comprends que je fais référence à la méthode equals(Tag) et non pas à la méthode equals(Object) qui aurait également pu convenir. Et ce parce que equals(Tag) est plus spécifique que equals(Object).

    @ Rei Ichido : si j'ai bien compris ton explication, les HashSet<T> (et les collections en général) ne fonctionne pas tout à fait de la même façon car elles utilisent les fonctionnalités de la programmation générique. Lorsque les méthode de la classe HashSet appellent la méthode equals(T) un cast vers le type Object est implicitement effectué. (Mon livre parle du principe d'effacement, erasure en anglais). Par conséquent seule la méthode equals(Object) peut être appelé par les méthodes des classes génériques.


    Ai-je bien compris vos explications ?

  8. #8
    Expert éminent sénior
    Avatar de tchize_
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Avril 2007
    Messages
    25 481
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    Localisation : Belgique

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Avril 2007
    Messages : 25 481
    Points : 48 806
    Points
    48 806
    Par défaut
    sauf qu'il n'y a pas de "cast". Il fuat comprendre que le generics ne sont pas des templates à la C++. On ne "recompile" pas hashset en disant "maintenant c'est Tag". HashSet a été compilé une fois pour tout avec "Object" et c'est tout. Les generics en java sont seulement du sucre syntaxique. Il permettent de garantir le typage des objets (je peux mettre que des "T" dans hashset et je ressortirait des "T") mais pour l'objet generic en lui même le type de T à la compilation est "Object". Avec ton generic, le compilateur va transformer


    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    HashSet<Tag> s = new Hashset<Tag>();
    s.add(new Tag());
    s.contains(tag2);
    Tag t = s.iterator().next();
    en
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    HashSet s = new Hashset();
    s.add(new Tag());
    s.contains(tag2);
    Tag t = (Tag)s.iterator().next();
    Mais, le compilateur a pu garantir que le casting ne pourra pas foirer.

  9. #9
    Nouveau membre du Club
    Profil pro
    Inscrit en
    Décembre 2010
    Messages
    20
    Détails du profil
    Informations personnelles :
    Âge : 38
    Localisation : France, Nord (Nord Pas de Calais)

    Informations forums :
    Inscription : Décembre 2010
    Messages : 20
    Points : 26
    Points
    26
    Par défaut
    Ok, merci à tous les deux.
    Histoire de mieux comprendre les classes génériques en Java je vais reprogrammer la classe HashSet en utilisant d'un côté les fonctionnalités de la programmation générique, de l'autre en programmant une classe HashSet<Tag> en dur (sans programmation générique).

    --> Résolu

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Générer (en code JAVA) la méthode equals dans les classes bindées par JAXB2
    Par greatmaster1971 dans le forum Format d'échange (XML, JSON...)
    Réponses: 0
    Dernier message: 11/12/2013, 16h55
  2. fonctionnent de la méthode run dans les threads
    Par L4BiN dans le forum Concurrence et multi-thread
    Réponses: 8
    Dernier message: 25/07/2006, 11h06
  3. Réponses: 3
    Dernier message: 13/06/2006, 14h52
  4. BINARY, NOT IN, NOT EQUAL... et les Index !
    Par Acti dans le forum Requêtes
    Réponses: 2
    Dernier message: 23/03/2006, 17h22
  5. Les méthodes abstraites et les interfaces
    Par ETI-trian dans le forum Langage
    Réponses: 3
    Dernier message: 17/01/2006, 12h14

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo