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 :

Classe Key encapsulant une chaîne de caractères


Sujet :

avec Java

  1. #1
    Membre du Club
    Profil pro
    Inscrit en
    Août 2008
    Messages
    62
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2008
    Messages : 62
    Points : 59
    Points
    59
    Par défaut Classe Key encapsulant une chaîne de caractères
    Bonjour,

    J'ai comme exercice à faire l'implémentation d'une classe représentant une "clef". Une clef étant une chaîne de caractères :
    • pouvant comporter des espaces et tabulations en début et/ou fin de chaîne
    • composées de lettres, chiffres et underscore


    Lors de la comparaison de deux clefs, on ne doit pas tenir compte ni des espaces, ni des tabulations, ni de la casse
    Également, on doit conserver (et pouvoir retourner) la valeur initiale de la clef

    Ci-dessous la classe telle que je l'ai implémentée :
    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
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    package Config;
     
    public class Key implements Comparable<Key>
    {
        private String _value;
     
        public String getValue()
        {
            return this._value;
        }
     
        private void setValue( String value )
        {
            this._value = value;
        }
     
        public int compareTo(Key ck)
        {
            return this._value.trim().toUpperCase().compareTo(ck.getValue().trim().toUpperCase());
        }
     
        @Override
        public boolean equals(Object o)
        {
            if( o == this ) return true;
            if( ! ( o instanceof Key ) ) return false;
            return 0 == this.compareTo(((Key) o) );
        }
     
        @Override
        public int hashCode()
        {
            return this._value.trim().toUpperCase().hashCode();
        }
     
        /**
         * Constructeur
         * @param value valeur
         * @throws Exception si format incorrect
         */
        public Key(String value) throws Exception
        {
            if( ! value.matches("^[ \t]*[a-zA-Z0-9_]+[ \t]*$") ) throw new Exception("Clef invalide : [" + value + "]");
     
            this.setValue( value );
        }
    }
    Questions :
    1. L'EDI m'indique un warning s'agissant de la méthode compareTo() : il me suggère d'ajouter l'annotation @Override, ce qui ne me semble pas pertinent (Object n'implémente pas compareTo(), donc je ne vois pas en quoi il y aurait surcharge)
    2. S'agissant de la méthode equals(), peut-on passer plutôt un argument de type Key ?
    3. L'interface Comparable<T> m'oblige à redéfinir compareTo() : existe-t-il une interface obligeant à redéfinir equals() et hashCode() ? Également, quand est-il nécessaire de redéfinir ces deux méthodes ? Si je commente compareTo() et/ou equals() et/ou hashCode() et que j'utilise mon petit programme de test fourni en fin de post, j'arrive aux conclusions suivantes : ArrayList.sort() utilise compareTo(), ArraySort.contains() se base plutôt sur equals()… Finalement, je peux toujours implémenter systématiquement les 3 fonctions, et c'est sans doute une bonne pratique, mais pour ma culture j'aimerais savoir quand l'une ou l'autre est utilisée ? Peut-être existe-t-il une interface imposant de redéfinir le package des 3 fonctions ?
    4. Au niveau de mon programme de test, si je remplace if( 0 == arrKey.get(i).compareTo(arrKey.get(j)) ) par if( arrKey.get(i) == arrKey.get(j) ) : ça compile, mais à l'exécution c'est tout faux : aucune égalité dans les clefs : sans doute parce qu'il y a comparaison de "pointeurs" ? Pour que ça fonctionne, il faudrait en plus une surcharge de l'opérateur "==" comme en C# ?
    5. Globalement, que pensez-vous du code de la classe Key ? Ai-je loupé quelque chose qui aurait rendu ce code plus fiable / élégant / … ?


    D'avance merci pour vos remarques bienvenues.


    Petit programme de test :
    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
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    package testkey;
     
    import java.util.*;
    import Config.Key;
     
    public class TestKey
    {
        public static void main(String[] args) throws Exception
        {
            ArrayList<Key> arrKey = new ArrayList<Key>();
     
            final String[] arrStr = {
                "CLEF_1",
                "\tCLEF_2",
                "CLEF_3\t",
                "Clé incorrecte",
                "\t\t\tclef_1",
                "       ClEf_2\t\t\t",
                " \t cLeF_3\t \t"
            };
     
            for(String str : arrStr )
            {
                System.out.print("Test chaine \"" + str + "\" : ");
                try
                {
                    arrKey.add( new Key( str ) );
                    System.out.println("est une clef valide");
                }
                catch( Exception e )
                {
                    System.out.println("n'est PAS une clef valide : " + e.toString() );
                }
            }
     
            System.out.println( "--- COMPARAISON ---" );
     
            for( int i=0; i<arrKey.size(); i++ )
            {
                System.out.println( "Clé(s) égale(s) à \"" + arrKey.get(i).getValue() + "\"");
                for( int j=0; j<arrKey.size(); j++ )
                {
                    if( i == j ) continue;
                    if( 0 == arrKey.get(i).compareTo(arrKey.get(j)) ) 
                    {
                        System.out.println("\t\t >>> \"" + arrKey.get(j).getValue() + "\"");
                    }
                }
            }
     
            System.out.println( "--- TRI ---" );
            arrKey.sort(null);
            for( Key k : arrKey )
            {
                System.out.println( k.getValue() );
            }
     
            Key keyTestContains = new Key( "\t\t\t\tCLef_2    ");
            System.out.print( "*** Test contains : " );
            System.out.println( arrKey.contains( keyTestContains ) ? "OK" : "pas ok..." );
        }
    }

  2. #2
    Membre averti
    Homme Profil pro
    Dev
    Inscrit en
    Novembre 2006
    Messages
    112
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : France, Alpes Maritimes (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Dev

    Informations forums :
    Inscription : Novembre 2006
    Messages : 112
    Points : 350
    Points
    350
    Par défaut
    Salut

    mon EDI pleurniche (warning uniquement, ça compile) s'agissant de la méthode compareTo() : il me suggère d'ajouter l'annotation @Override, ce qui ne me semble pas pertinent (Object n'implémente pas compareTo(), donc je ne vois pas en quoi il y aurait surcharge)
    l'annotation @Override signale que la methode est définie dans un classe mere ou que son prototype est définie dans une interface ( dans ton cas Comparable )

    s'agissant de la méthode equals(), peut-on passer plutôt un argument de type Key ?
    il faut que tu redefinissent la methode equals avec comme type Object.
    mais tu peut avoir en plus une methode equals qui prend Key ou autre

    l'interface Comparable<T> m'oblige à redéfinir compareTo() : existe-t-il une interface obligeant à redéfinir equals() et hashCode() ? Également, quand est-il nécessaire de redéfinir ces deux méthodes ? Si je commente compareTo() et/ou equals() et/ou hashCode() et que j'utilise mon petit programme de test fourni en fin de post, j'arrive aux conclusuons suivantes : ArrayList.sort() utilise compareTo(), ArraySort.contains() se base plutôt sur equals()… Finalement, je peux toujours implémenter systématiquement les 3 fonctions, et c'est sans doute une bonne pratique, mais pour ma culture j'aimerais savoir quand l'une ou l'autre est utilisée ? Peut-être existe-t-il une interface imposant de redéfinir le package des 3 fonctions ?
    la methode compareTo() sert à ordonnée 2 élément ( quand tu fait un tri) alors que la méthode equals sert à tester si 2 éléments sont égales .

    en général , on redefinie hashCode() et equals en meme temps.
    tu doit obligatoire respecter les règles suivantes;

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    // o1 et o2 sont deux instance de classe
    si tu as o1==o2 alors o1.equals(o2)==true
    si tu as o1.hashcode()!= o2.hashcode()  alors o1.equals(o2)==false
    si tu as  o1.equals(o2)==true alors o1.hashcode()== o2.hashcode()


    au niveau de mon programme de test, si je remplace if( 0 == arrKey.get(i).compareTo(arrKey.get(j)) ) par if( arrKey.get(i) == arrKey.get(j) ) : ça compile, mais à l'exécution c'est tout faux : aucune égalité dans les clefs : sans doute parce qu'il y a comparaison de "pointeurs" ? Pour que ca fonctionne, il faudrait en plus une surcharge de l'opérateur "==" comme en C# ?
    la surcharge d'operateur n'existe pas en Java .


    Globalement, que pensez-vous du code de la classe Key ? Ai-je loupé quelque chose qui aurait rendu ce code plus fiable / élégant / … ?
    Tu peux aussi ajouter un attribut String valuetrim qui contient la valeur value.trim().toUpperCase() que tu évalue à chaque changement de value pour ne pas l’évaluer a chaque fois.

    A titre d'information

    *la classe String
    • possede un methode equalsIgnoreCase qui teste l'egalite en ignorant la case;
    • definie un Compator ( String.CASE_INSENSITIVE_ORDER ) qui ignore la case;

  3. #3
    Modérateur

    Profil pro
    Inscrit en
    Septembre 2004
    Messages
    12 551
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Septembre 2004
    Messages : 12 551
    Points : 21 607
    Points
    21 607
    Par défaut
    Bon c'est pas mal, mais je vais apporter des précisions.

    Citation Envoyé par Isidore.76 Voir le message
    mon EDI pleurniche (warning uniquement, ça compile) s'agissant de la méthode compareTo() : il me suggère d'ajouter l'annotation @Override, ce qui ne me semble pas pertinent (Object n'implémente pas compareTo(), donc je ne vois pas en quoi il y aurait surcharge)
    À noter que override ne veut pas dire surcharge, ça se serait overload et il n'y a pas d'annotation pour ça. Override veut dire redéfinition et... Ce n'est pas le cas non plus. Object ne définissant pas compareTo(), il n'y a pas redéfinition, il n'y a que définition.

    Le nom Override est malheureux. Il semble dire que c'est juste pour les redéfinitions alors qu'en fait, c'est à chaque fois que la méthode représente en fait une méthode d'un (ou plusieurs) de ses types ascendants : classes ou interfaces.
    Malgré ce nom trompeur, elle est par contre très pertinente : elle annonce qu'on veut que la méthode reprenne une méthode déjà déclarée quelque part, et pas faire une nouvelle méthode qui n'existait pas avant. Le compilateur nous préviendra donc si cette méthode ne reprend pas une méthode existante, par exemple si on s'est trompé en écrivant son nom, ou dans la liste de ses paramètres, ou si on avait juste halluciné l'existence d'une méthode.

    Citation Envoyé par Isidore.76 Voir le message
    s'agissant de la méthode equals(), peut-on passer plutôt un argument de type Key ?
    Non, ça du coup ce serait une surcharge : une autre méthode qui n'a rien à voir avec la méthode equals() déjà existante, à part le fait inintéressant qu'elles ont le même nom. Elles n'ont pas la même signature, donc elle n'ont aucun lien.
    equals(Object) existe déjà, implémentée par Object donc toutes les classes du monde, redéfinie par beaucoup. Donc les autres classes connaissent cette méthode et peuvent s'en servir. Elles ne connaîtront pas equals(Key), ce qui la rend sans intérêt.

    Citation Envoyé par Isidore.76 Voir le message
    l'interface Comparable<T> m'oblige à redéfinir compareTo() : existe-t-il une interface obligeant à redéfinir equals() et hashCode() ?
    Hélas non. Ces méthodes étant déjà définies par Object, le compilateur n'exige jamais de redéfinition.
    Dans certains cas on a par contre besoin de le faire pour que certains objets puissent utiliser la classe correctement. En effet celles définies par Object disent que deux objets sont égaux quand... Ben en fait deux objets ne sont jamais égaux, equals() ne renvoyant true que si on compare un objet à lui-même de la même manière que == le fait déjà.
    D'autres classes veulent que deux de leurs instances soient considérées égales si elles représentent le même contenu, pour ça elles redéfinissent equals() et hashCode(). Par exemple la classe String les redéfinit pour que deux objets String distincts soient considérés égaux quand ils contiennent les même caractères dans le même ordre.

    Citation Envoyé par Isidore.76 Voir le message
    Également, quand est-il nécessaire de redéfinir ces deux méthodes ? Si je commente compareTo() et/ou equals() et/ou hashCode() et que j'utilise mon petit programme de test fourni en fin de post, j'arrive aux conclusuons suivantes : ArrayList.sort() utilise compareTo(), ArraySort.contains() se base plutôt sur equals()… Finalement, je peux toujours implémenter systématiquement les 3 fonctions, et c'est sans doute une bonne pratique,
    Ça dépend. De tout.

    String, Date, Integer, tout ça ne servirait pas à grand-chose si ça n'implémentait pas equals(), hashCode() et Comparable. Ce sont des classes représentant des données, presque des structures simples, et dont le besoin de comparer ces données à d'autres est évident.

    Pour d'autres, comme un Set, une List ou une Map, il reste l'idée de les comparer avec d'autres pour voir s'ils ont le même contenu, avec equals() et hashCode()... Mais l'idée d'une relation d'ordre avec compareTo() n'a pas de sens.

    Pour d'autres enfin, aucune comparaison n'a de sens, sauf à la rigueur pour voir si les deux variables vont en fait vers un seul objet. InputStream, Runnable, ThreadPoolExecutor, Comparator et tant d'autres, sont des classes fonctionnelles, qui servent à exécuter un comportement et pas à décrire des données. Tout ça n'a pas de relation d'égalité, et ne redéfinissent aucune méthode de comparaison.

    C'est une bonne pratique d'implémenter les méthodes de comparaison, lorsque cela a un sens évident. Le reste du temps, au moins ça se discute, voire c'est ridicule.

    Citation Envoyé par Isidore.76 Voir le message
    mais pour ma culture j'aimerais savoir quand l'une ou l'autre est utilisée ?
    Vu que equals() et hashCode() sont implémentées par tous les objets, n'importe quelle classe est libre de s'en servir, même s'il vaut quand même mieux qu'elle précise qu'elle le fait (car sinon on risque de ne pas savoir que les classes qu'on lui envoit ont besoin que equals() et hashCode() aient un sens précis.)
    Pareil pour Comparable, cette interface étant très connue, n'importe quelle classe peut décider d'accepter des comparable pour exploiter leur relation d'ordre.

    En pratique, les plus connus :
    - la plupart des Collections utilisent equals() pour leur méthode contains(). Toutes les List, qui s'en servent aussi pour indexOf() et lastIndexOf(), et aussi Map.containsValue().
    - Les HashSet, HashMap et similaires utilisent hashCode() pour, ben pour faire le hachage des objets qu'ils contiennent. Comme ça ne suffit pas puisque deux objets peuvent en théorie avoir le même hashCode() sans être égaux, elles utilisent ensuite equals() pour vérifier. C'est pour ça que hashCode() et equals() doivent s'efforcer d'avoir un comportement cohérent : sinon HashSet, HashMap et similaires feront n'importe quoi.
    - Les SortedSet, SortedMap et similaires n'utilisent aucun des deux, les remplaçant par compareTo() si les objets implémentent Comparable, ou par le compare() d'un Comparator qu'on leur a fourni à la construction. Du coup il n'est pas nécessaires qu'ils soient cohérents avec equals() et hashCode(). Mais en général, ne pas l'être serait contre-intuitif.
    -- Il y a aussi l'évident Collections.sort(List) qui trie une liste en rangeant les éléments par ordre croissant ou décroissant.

    Citation Envoyé par Isidore.76 Voir le message
    Peut-être existe-t-il une interface imposant de redéfinir le package des 3 fonctions ?
    Non. hashCode() et equals() étant implémentées par Object, elles sont déjà implémentées par tous les objets et aucun mécanisme ne peut forcer à les redéfinir.

    Citation Envoyé par Isidore.76 Voir le message
    au niveau de mon programme de test, si je remplace if( 0 == arrKey.get(i).compareTo(arrKey.get(j)) ) par if( arrKey.get(i) == arrKey.get(j) ) : ça compile, mais à l'exécution c'est tout faux : aucune égalité dans les clefs : sans doute parce qu'il y a comparaison de "pointeurs" ?
    Oui. En Java il n'y a pas tellement de pointeurs, c'est plutôt que quand on fait == sur deux objets on cherche à vérifier si c'est en fait le même objet. Mais dans le cas présent c'est la même chose.

    Citation Envoyé par Isidore.76 Voir le message
    Pour que ca fonctionne, il faudrait en plus une surcharge de l'opérateur "==" comme en C# ?
    Comme on ne peut pas redéfinir d'opérateur en Java, à la place on utilise equals(). C'est officiellement comme ça qu'on fait.

    Citation Envoyé par miaous Voir le message
    A titre d'information

    *la classe String
    • possede un methode equalsIgnoreCase qui teste l'egalite en ignorant la case;
    • definie un Compator ( String.CASE_INSENSITIVE_ORDER ) qui ignore la case;
    Exactement, ce qui évite aussi un problème peu connu et navrant de toUpperCase()/toLowerCase(), c'est qu'elles dépendent de la locale par défaut. Par exemple en turc, la lettre majuscule I, son équivalent minuscule est ı, un i sans point. Sur des machines configurées en langue turque, ces méthodes appliquent par défaut les règles turques.
    Ce n'est pas le cas de equalsIgnoreCase() ou CASE_INSENSITIVE_ORDER, qui considèrent plutôt les String comme conteneurs d'un langage informatique universel. Ce qui est exactement ce qu'on imaginerait de prime abord, la gestion des langues étant normalement déportée vers des classes plus haut niveau.

    Citation Envoyé par Isidore.76 Voir le message
    que pensez-vous du code de la classe Key ? Ai-je loupé quelque chose qui aurait rendu ce code plus fiable / élégant / … ?
    Est-elle vraiment utile, au fond ? Ne sont-ce pas de simple Strings vaguement prévérifiées ?

    Tu aurais pu avoir d'une part, une méthode qui vérifie la validité d'une String en temps que clé, et d'autre part un Comparator<String> qui comparent deux String selon les règles des clés. Le tout en continuant de traiter avec des String normales et en profitant de toutes leurs capacités déjà là.
    Ce n'est pas forcément mieux. Une classe dédiée a le mérite de typer très précisément que ta clé est une clé et pas, par exemple, un message d'erreur ou un identifiant de session. Mais simplement donner un nom aux variables clés qui rendent évident que ce sont des clés, est souvent suffisant.
    N'oubliez pas de consulter les FAQ Java et les cours et tutoriels Java

  4. #4
    Expert éminent sénior
    Avatar de adiGuba
    Homme Profil pro
    Développeur Java/Web
    Inscrit en
    Avril 2002
    Messages
    13 938
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur Java/Web
    Secteur : Transports

    Informations forums :
    Inscription : Avril 2002
    Messages : 13 938
    Points : 23 190
    Points
    23 190
    Billets dans le blog
    1
    Par défaut
    Salut,


    Je rajouterais quelques remarques sur le code lui-même.

    • Il serait préférable de rendre cette classe immuable. C'est plus pratique en multithread, pour partager les instances ou pour l'utiliser en clef d'une Map.
      Pour cela il suffit de virer le setter et de mettre la classe et l'attribut en final.
    • Tu te traines des trim().toUpperCase() un peu partout c'est affreux !
      Si tu vas comparer 1000 éléments entre eux tu vas sûrement passer plus de temps à faire cela qu'autre chose.
      C'est lourd car cela peut générer de nouvelles instance à chaque fois.
      Tu devrais faire cela une fois pour toute dans le constructeur.
    • Le constructeur qui remonte une "Exception" c'est lourd et pas pratique (ni clair).
      Il vaut mieux remonter une unchecked-exception comme IllegalArgumentException...
    • C'est juste un détail mais perso je n'utiliserais pas compareTo() dans equals()...
    • Tu n'acceptes les lettres majuscules avec accents. C'est volontaire ?
    • La création d'un pattern peut être "lourd". Si tu dois créer beaucoup d'instance il serait préférable d'en créer un seul.


    Exemple :
    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
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    public final class Key implements Comparable<Key> {
     
    	private static final Pattern PATTERN = Pattern.compile("^[A-Z0-9_]+$");
     
    	private final String _value;
     
    	public Key(String value) throws IllegalArgumentException {
    		this._value = value.trim().toUpperCase();
     
    		if (!PATTERN.matcher(value).matches())
    			throw new IllegalArgumentException("Clef invalide : [" + value + "]");
     
     
    	}
     
    	public String getValue() {
    		return this._value;
    	}
     
    	public int compareTo(Key ck) {
    		return this._value.compareTo(ck._value);
    	}
     
    	@Override
    	public boolean equals(Object o) {
    		if (o == this)
    			return true;
    		if (!(o instanceof Key))
    			return false;
    		Key other = (Key) o;
    		return this._value.equals(other._value);
    	}
     
    	@Override
    	public int hashCode() {
    		return this._value.hashCode();
    	}
    }

    a++

  5. #5
    Membre du Club
    Profil pro
    Inscrit en
    Août 2008
    Messages
    62
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2008
    Messages : 62
    Points : 59
    Points
    59
    Par défaut
    Miaous, thelvin, adiGuba : merci pour vos réponses détaillées et instructives !

    J'ai donc modifié la classe Key en tenant largement compte de vos remarques.

    Au cas où, en fin de post : la classe, le petit programme de test, et la sortie console correspondante.

    Il me faudra creuser du coté de equalsIgnoreCase() et CASE_INSENSITIVE_ORDER.

    Reste une question : la différence entre exception et unchecked-exception tient à ce que les exceptions checked forcent l'appelant à traiter ou propager l'exception (ce qui n'est pas le cas pour les exceptions unchecked) ; s'il survient une erreur dans le constructeur de Key, il me semble nécessaire que l'appelant soit informé de la possibilité et qu'il s'en préoccupe ; aussi ai-je maintenu mon choix initial d'une exception checked – sauf unanimité plutôt contre

    Encore merci à vous trois


    Classe Key :
    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
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    package Config;
     
    public final class Key implements Comparable<Key>
    {
        private String _value;
        private String _internalValue;
        private int _hashCode;
     
        /*
        ** getter et setter
        */
        public String getValue()
        {
            return this._value;
        }
     
        private void setValue(String value)
        {
            this._value = value;
            this._internalValue = value.trim().toUpperCase().replace("\t", "");
            this._hashCode = this._internalValue.hashCode();
        }
     
        public String getInternalValue()
        {
            return this._internalValue;
        }
     
        protected int getHashCode()
        {
            return this._hashCode;
        }
     
        /*
        ** Comparable<Key>
         */
        @Override
        public int compareTo(Key ck)
        {
            return this.getInternalValue().compareTo(ck.getInternalValue());
        }
     
        @Override
        public boolean equals(Object o)
        {
            if( o == this ) return true;
            if( ! ( o instanceof Key ) ) return false;
     
            Key other = (Key) o;
            return this.getInternalValue().equals( other.getInternalValue() );
        }
     
        @Override
        public int hashCode()
        {
            return this.getHashCode();
        }
     
        @Override
        public String toString()
        {
            return String.format( 
                    "Clef interne : [%s] - Hash : [%s] - Clef : [%s] ",
                    this.getInternalValue(), 
                    this.getHashCode(),
                    this.getValue()
            );
        }
     
        /**
         * Constructeur
         * @param value valeur
         * @throws Exception si format incorrect
         */
        public Key(String value) throws Exception 
        {
            if( ! value.matches("^[ \t]*[a-zA-Z0-9_]+[ \t]*$") )
            {
                throw new Exception ("Clef invalide : [" + value + "]");
            }
     
            this.setValue(value);
        }
    }
    Programme de test :
    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
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    package testkey;
     
    import java.util.*;
    import Config.Key;
     
    public class TestKey
    {
        public static void main(String[] args) throws Exception
        {
            ArrayList<Key> arrKey = new ArrayList<Key>();
     
            final String[] arrStr = {
                "CLEF_1",
                "\tCLEF_2",
                "CLEF_3\t",
                "Clé incorrecte",
                "\t\t\tclef_1",
                "       ClEf_2\t\t\t",
                " \t cLeF_3\t \t"
            };
     
            /*
            ** Initialisation
            */
            System.out.println("***\n*** Initialisation\n***");
            for(String str : arrStr )
            {
                try
                {
                    arrKey.add( new Key( str ) );
                }
                catch( Exception e )
                {
                    System.out.println( e.toString() );
                }
            }
     
            for( Key k : arrKey )
            {
                System.out.println( k.toString() );
            }
     
            /*
            ** Test equals()
            */
            System.out.println( "***\n*** Test equals()\n***" );
     
            for( Key k1 : arrKey )
            {
                for( Key k2 : arrKey )
                {
                    if( k1 == k2 ) continue;
                    if( ! k1.equals(k2) ) continue;
                    System.out.println( String.format( "[%s] equals [%s]", k1.getValue(), k2.getValue() ) );
                }
            }
     
            /*
            ** Test compareTo() via sort()
            */
            System.out.println("***\n*** Test compareTo() via sort()\n***");
            arrKey.sort(null);
            for( Key k : arrKey )
            {
                System.out.println( k.toString() );
            }
     
            /*
            ** Test contains() pour la clef "\t\t\t\tCLef_2    "
            */
            System.out.println( "***\n*** Test contains() pour la clef \"\\t\\t\\t\\tCLef_2    \"\n***" );
            Key keyTestContains = new Key( "\t\t\t\tCLef_2    ");
            System.out.println( arrKey.contains( keyTestContains ) ? "OK" : "pas ok..." );
        }
    }
    Sortie console :
    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
    24
    25
    26
    27
    28
    29
    30
    31
    32
    ***
    *** Initialisation
    ***
    java.lang.Exception: Clef invalide : [Clé incorrecte]
    Clef interne : [CLEF_1] - Hash : [1990466556] - Clef : [CLEF_1] 
    Clef interne : [CLEF_2] - Hash : [1990466557] - Clef : [	CLEF_2] 
    Clef interne : [CLEF_3] - Hash : [1990466558] - Clef : [CLEF_3	] 
    Clef interne : [CLEF_1] - Hash : [1990466556] - Clef : [			clef_1] 
    Clef interne : [CLEF_2] - Hash : [1990466557] - Clef : [       ClEf_2			] 
    Clef interne : [CLEF_3] - Hash : [1990466558] - Clef : [ 	 cLeF_3	 	] 
    ***
    *** Test equals()
    ***
    [CLEF_1] equals [			clef_1]
    [	CLEF_2] equals [       ClEf_2			]
    [CLEF_3	] equals [ 	 cLeF_3	 	]
    [			clef_1] equals [CLEF_1]
    [       ClEf_2			] equals [	CLEF_2]
    [ 	 cLeF_3	 	] equals [CLEF_3	]
    ***
    *** Test compareTo() via sort()
    ***
    Clef interne : [CLEF_1] - Hash : [1990466556] - Clef : [CLEF_1] 
    Clef interne : [CLEF_1] - Hash : [1990466556] - Clef : [			clef_1] 
    Clef interne : [CLEF_2] - Hash : [1990466557] - Clef : [	CLEF_2] 
    Clef interne : [CLEF_2] - Hash : [1990466557] - Clef : [       ClEf_2			] 
    Clef interne : [CLEF_3] - Hash : [1990466558] - Clef : [CLEF_3	] 
    Clef interne : [CLEF_3] - Hash : [1990466558] - Clef : [ 	 cLeF_3	 	] 
    ***
    *** Test contains() pour la clef "\t\t\t\tCLef_2    "
    ***
    OK

  6. #6
    Expert éminent sénior
    Avatar de adiGuba
    Homme Profil pro
    Développeur Java/Web
    Inscrit en
    Avril 2002
    Messages
    13 938
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur Java/Web
    Secteur : Transports

    Informations forums :
    Inscription : Avril 2002
    Messages : 13 938
    Points : 23 190
    Points
    23 190
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par Isidore.76 Voir le message
    Reste une question : la différence entre exception et unchecked-exception tient à ce que les exceptions checked forcent l'appelant à traiter ou propager l'exception (ce qui n'est pas le cas pour les exceptions unchecked) ; s'il survient une erreur dans le constructeur de Key, il me semble nécessaire que l'appelant soit informé de la possibilité et qu'il s'en préoccupe ; aussi ai-je maintenu mon choix initial d'une exception checked – sauf unanimité plutôt contre
    A titre perso je considère les checked-exceptions comme la plus mauvaise idée de Java.
    Pourquoi forcer les devs à traiter une exception à tout prix ? Dans bien des cas cela aboutit à de mauvais traitement...


    Du coup tu te dans l'impossibilité de faire un code tout simple comme cela, car il faut à tout prix traiter une exception qui ne surviendra jamais :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    	Key key = new Key("KEY1");
     
    	// gros code qui utilise key

    Les unchecked-exceptions laissent les devs plus libre vis à vis de cela...




    Sinon pas la peine de stocker le hashCode pour éviter de le recalculer à chaque fois : la méthode String::hashCode() le fait déjà


    a++

  7. #7
    Membre du Club
    Profil pro
    Inscrit en
    Août 2008
    Messages
    62
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2008
    Messages : 62
    Points : 59
    Points
    59
    Par défaut
    Citation Envoyé par adiGuba Voir le message
    A titre perso je considère les checked-exceptions comme la plus mauvaise idée de Java.
    Pourquoi forcer les devs à traiter une exception à tout prix ? Dans bien des cas cela aboutit à de mauvais traitement...
    Il s'agit, en tout cas, d'une différence avec le monde dotnet qui ne supporte qu'un seul genre d'exception (unchecked, du coup) !

    Citation Envoyé par adiGuba Voir le message
    Sinon pas la peine de stocker le hashCode pour éviter de le recalculer à chaque fois : la méthode String::hashCode() le fait déjà
    Bon à savoir !
    Lorsque je fais Alt+F1 sous mon EDI (Netbeans), et que je suis positionné sur hashCode(), hop mon navigateur s'ouvre sur la page : http://docs.oracle.com/javase/8/docs...tml#hashCode--
    Pratique, mais sur la page rien n'indique que le hashCode n'est pas systématiquement recalculé - même si on peut supposer qu'un peu d'optimisation voudrait qu'il soit calculé au premier appel de hashCode() et ensuite uniquement si la chaine a été modifiée...
    Il n'empêche, du coup, comment le savoir (simplement) ?

  8. #8
    Modérateur

    Profil pro
    Inscrit en
    Septembre 2004
    Messages
    12 551
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Septembre 2004
    Messages : 12 551
    Points : 21 607
    Points
    21 607
    Par défaut
    Citation Envoyé par Isidore.76 Voir le message
    Reste une question : la différence entre exception et unchecked-exception tient à ce que les exceptions checked forcent l'appelant à traiter ou propager l'exception (ce qui n'est pas le cas pour les exceptions unchecked) ; s'il survient une erreur dans le constructeur de Key, il me semble nécessaire que l'appelant soit informé de la possibilité et qu'il s'en préoccupe
    Si vraiment tu trouves que c'est plus un avantage qu'un inconvénient, crée au moins une nouvelle classe exception dédiée à ça, genre InvalidKeyException, ou alors utilise à la rigueur ParseException parce que la clé n'a pas pu être parsée.
    Juste Exception ça engobe tout et n'importe quoi, pas juste l'erreur que tu cherchais à gérer.

    Citation Envoyé par Isidore.76 Voir le message
    Lorsque je fais Alt+F1 sous mon EDI (Netbeans), et que je suis positionné sur hashCode(), hop mon navigateur s'ouvre sur la page : http://docs.oracle.com/javase/8/docs...tml#hashCode--
    Pratique, mais sur la page rien n'indique que le hashCode n'est pas systématiquement recalculé - même si on peut supposer qu'un peu d'optimisation voudrait qu'il soit calculé au premier appel de hashCode() et ensuite uniquement si la chaine a été modifiée...
    Il n'empêche, du coup, comment le savoir (simplement) ?
    Dans le mien (Eclipse), si j'appuie sur Control et que je clique sur l'appel d'une méthode (ici hashCode()) ça m'ouvre le code source de la classe qui l'implémente, et me montre direct l'implémentation de cette méthode. Ici la classe String, dont le code source est fourni avec le JDK. Je peux y voir, en faisant un peu attention, qu'il y a une variable membre qui retient le hashcode s'il a déjà été calculé, et qu'il n'est calculé que s'il ne l'a pas déjà été. Éventuellement si plusieurs threads l'appellent en même temps les deux peuvent se retrouver à le calculer en même temps, mais ce n'est pas bien grave puisque ça fera le même résultat.

    Du coup ça veut dire qu'une autre implémentation que celle dont je viens de regarder le code source, pourrait avoir été implémentée différemment. C'est possible, oui. Puisque ce n'est pas documenté.
    Du coup ne vaut-il pas mieux le faire soi-même au cas où une implémentation de JDK qui n'est pas la même que la mienne, ne le fasse pas puisque la documentation ne l'impose pas ?
    Hum, peut-être. Sauf que calculer un hashCode(), au fond, n'est pas une opération coûteuse par rapport au reste d'un programme. On peut se demander à quoi sert cette optimisation. Le JDK la fait, parce qu'il existe bel et bien des programmes où String.hashCode() représente une grosse partie du boulot. Mais le tien, je ne pense pas.
    N'oubliez pas de consulter les FAQ Java et les cours et tutoriels Java

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

Discussions similaires

  1. Réponses: 8
    Dernier message: 12/02/2013, 01h08
  2. Réponses: 3
    Dernier message: 17/08/2011, 14h40
  3. [Debutant(e)] Analyse d'une chaîne de caractères
    Par maire106 dans le forum Langage
    Réponses: 6
    Dernier message: 22/03/2004, 15h04
  4. Inverser une chaîne de caractères
    Par DBBB dans le forum Assembleur
    Réponses: 2
    Dernier message: 30/03/2003, 11h09
  5. Réponses: 3
    Dernier message: 09/05/2002, 01h39

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