Skip Navigation Links
Accueil
Java Standard EditionExpand Java Standard Edition
Java EE 5Expand Java EE 5
Visual Basic .Net 2005Expand Visual Basic .Net 2005
Visual C++ .Net 2005Expand Visual C++ .Net 2005
Visual C# .Net 2005Expand Visual C# .Net 2005
Cours ASP .Net 2.0Expand Cours ASP .Net 2.0
PostgresqlExpand Postgresql
LinuxExpand Linux
Visual Studio 2008Expand Visual Studio 2008
ASP 3.0 ClassiqueExpand ASP 3.0 Classique
Cours Javascript - DOM - DHTMLExpand Cours Javascript - DOM - DHTML
Cours AjaxExpand Cours Ajax
VBAExpand VBA
AssembleurExpand Assembleur
PerlExpand Perl
MembresExpand Membres
L'auteur du site
Nouveautés sur le site
Contacts
Plan du site
Accueil > Java Standard Edition > La classe String (java.lang)
____________________________________________________________________________________________________
Connexion

La classe String (java.lang)

Sommaire :

I) Généralités
II) Le fonctionnement de la classe String (java.lang)
   II-1) L'interface CharSequence (java.lang)
   II-2) L'interface Comparable (java.lang)
   II-3) L'interface Serializable (java.io)
   II-4) Attributs private de String
   II-5) Constructeurs de String
   II-6) Des méthodes de String
   II-7) la méthode hashCode de String - Définition rapide du hashcode

Ce chapitre constitue des remarques sur l'implémentation de la classe String.

I) Généralités

" The String class represents character strings. All string literals in Java programs, such as "abc", are implemented as instances of this class.

Strings are constant; their values cannot be changed after they are created. String buffers support mutable strings. Because String objects are immutable they can be shared. For example:

String str = "abc";
is equivalent to:

char data[] = {'a', 'b', 'c'};
String str = new String(data);
"

La classe String représente les chaînes de caractères. Les littéraux chaînes de caractères dans les programmes Java, comme "abc", sont implémentés comme des instances de cette classe.
Les String sont constantes; leur valeur ne peut pas être changée après qu'elles ont été créées. La classe StringBuffer supporte les chaînes muables. Parce que les objets String sont immuables, ils peuvent être partagés.

Ne croyez pas que, parce qu'on peut écrire
que les String peuvent être modifiées. Lorsqu'on écrit "maChaine", on crée un objet String en mémoire, et on met sa référence dans la variable varChaine. Puis, en écrivant varChaine="nouvelle", l'objet String "maChaine" reste en mémoire, mais n'est plus référencé par personne (il sera donc détruit par le garbage collector quand il passera).
Un nouvel objet String, valant "nouvelle", est créé en mémoire, puis sa référence est mise dans la variable varChaine. On n'a donc pas pu modifier varChaine, en réalité, on a juste mis dans varChaine un autre objet String.

"The class String includes methods for examining individual characters of the sequence, for comparing strings, for searching strings, for extracting substrings, and for creating a copy of a string with all characters translated to uppercase or to lowercase. Case mapping relies heavily on the information provided by the Unicode Consortium's Unicode 3.0 specification. The specification's UnicodeData.txt and SpecialCasing.txt files are used extensively to provide case mapping.

The Java language provides special support for the string concatenation operator ( + ), and for conversion of other objects to strings. String concatenation is implemented through the StringBuffer class and its append method. String conversions are implemented through the method toString, defined by Object and inherited by all classes in Java. For additional information on string concatenation and conversion, see Gosling, Joy, and Steele, The Java Language Specification.

Unless otherwise noted, passing a null argument to a constructor or method in this class will cause a NullPointerException to be thrown. "

La classe String inclut des méthodes pour examiner des caractères individuels de la séquence, pour comparer des String, pour rechercher des chaînes, pour extraire des sous-chaînes, et pour créer une copie de chaîne en ayant converti tous les caractères en majuscules ou minuscules.

La concaténation de String est implémentée dans la classe StringBuffer et sa méthode append.
Remarquez que StringBuffer stocke la valeur de sa chaîne aussi sous forme de tableau de caractères, et qu'il agrandit la taille de son tableau interne(en créant un tableau plus grand et en recopiant les caractères) en cas de concaténation avec append. Un nouvel objet tableau est donc en réalité créé, mais on peut dire que son tableau interne n'est pas immuable(même si c'est un nouvel objet, la référence du tableau interne change).
Et l'objet String est resté le même, alors qu'elle représente une autre chaîne; c'est juste l'objet pour son tableau interne qui a changé.
Alors que la référence du tableau interne d'une String ne changera jamais, pour un même objet String. Et surtout, pour un certain objet String initialisé au départ, on ne pourra jamais lui faire représenter une autre chaîne de caractères.

Les conversions en chaînes de caractères sont implémentées à travers la méthode toString, définie par la classe Object, qui est héritée par toutes les classes en Java.

II) Le fonctionnement de la classe String (java.lang)

Remarquez le "final", pour empêcher d'hériter de cette classe.

II-1) L'interface CharSequence (java.lang)

"A CharSequence is a readable sequence of char values. This interface provides uniform, read-only access to many different kinds of char sequences. A char value represents a character in the Basic Multilingual Plane (BMP) or a surrogate. Refer to Unicode Character Representation for details.

This interface does not refine the general contracts of the equals and hashCode methods. The result of comparing two objects that implement CharSequence is therefore, in general, undefined. Each object may be implemented by a different class, and there is no guarantee that each class will be capable of testing its instances for equality with those of the other. It is therefore inappropriate to use arbitrary CharSequence instances as elements in a set or as keys in a map.

Since:
1.4
"

Une CharSequence est une séquence lisible de caractères. Cette interface procure des moyens d'accès uniformes et read-only pour plusieurs types de séquences de caractères.

Cette interface ne procure pas de méthodes de comparaison ou de hashcode. Le résultat de la comparaison de deux objets implémentant CharSequence est, par conséquent, indéfini. Chaque objet de la classe CharSequence peut être implémenté par une classe différente, et il n'y a aucune garantie pour chaque classe qu'elle soit capable de tester, pour ses instances, l'égalité avec les instances de l'autre classe.

On comprend cette explication, comment implémenter dans une classe, une méthode de comparaison, capable de prendre en compte toutes les classes CharSequence, alors que de nouvelles pourront toujours être créées dans le futur?

Revenons sur le début de cette javadoc: une CharSequence procure des moyens d'accès uniforme. Prenons l'exemple de deux classes implémentant CharSequence: String(java.lang) et StringBuffer(java.lang). CharSequence nous procure des moyens uniformes de lire ces différents types de strings.

Remarquons l'apparition tardive de CharSequence( depuis 1.4).

II-2) L'interface Comparable<T> (java.lang)

"This interface imposes a total ordering on the objects of each class that implements it. This ordering is referred to as the class's natural ordering, and the class's compareTo method is referred to as its natural comparison method.

Lists (and arrays) of objects that implement this interface can be sorted automatically by Collections.sort (and Arrays.sort). Objects that implement this interface can be used as keys in a sorted map or elements in a sorted set, without the need to specify a comparator
(...)"

Cette interface impose un total classement des objets de chaque classe qui l'implémentent. Ce classement est référencé comme étant le classement naturel de la classe, et la méthode compareTo est référencée comme étant sa méthode naturelle de comparaison.

Les listes( et tableaux) d'objets qui implémentent cette interface peuvent être triés automatiquement par Collections.sort (et Arrays.sort). Les objets qui implémentent cette interface peuvent être utilisés comme clés dans les map triées, ou comme éléments dans un ensemble trié, sans avoir besoin de spécifier un objet comparateur.

Il s'agit d'une interface générique. Pour la classe string, il s'agit de Comparable<String>. C'est-à-dire que String est obligé d'avoir une méthode public int compareTo( String o), qui compare donc la String courante et la String en paramètre.

Sur la méthode compareTo:
"Compares this object with the specified object for order. Returns a negative integer, zero, or a positive integer as this object is less than, equal to, or greater than the specified object.(...)"

La méthode compare l'objet courant avec l'objet spécifié en paramètre. Elle retourne un entier négatif, zéro, ou un entier positif selon que l'objet est inférieur, égal, ou supérieur à l'objet spécifié.

II-3) L'interface Serializable (java.io)

"(...) All subtypes of a serializable class are themselves serializable.The serialization interface has no methods or fields and serves only to identify the semantics of being serializable.(...)"

Tous les sous-types d'une classe sérialisable sont eux-mêmes sérialisables. L'interface de sérialisation n'a pas de méthodes ou d'attributs, et sert seulement à indiquer la sémantique du fait d'être sérialisable.

On remarque que l'interface ne fournit pas les moyens de sérialiser l'objet, car elle ne sert pas à donner la façon de le sérialiser, mais juste à indiquer qu'on accepte que cet objet soit sérialisé.

Pour sérialiser un objet (sérialisable), on utilise la méthode writeObject de ObjectOutputStream(java.io); pour le lire ensuite, on se sert de readObject de ObjectInputStream( java.io).

II-4) Attributs private de String

Regardons le début de la classe String:
Nous y voyons que la valeur interne de la String est en fait un tableau de caractères, nommé value.

offset est l'index de départ dans le tableau value.

count est le nombre de caractères de la chaîne.

II-5) Constructeurs de String

Regardons cet exemple de constructeur:
Ce constructeur permet de créer une string à partir d'un tableau de caractères.
L'attribut offset est mis à 0. count vaut la taille du tableau en paramètre.
Dans le tableau interne de notre string, on crée un nouveau tableau de caractères avec la méthode statique Arrays.copyOf ( java.util), initialisé avec le contenu du tableau en paramètre.

II-6) Des méthodes de String

Voyons quelques méthodes de la classe String, pour connaître son fonctionnement.
getChars utilise la méthode System.arraycopy, qui va recopier le contenu du tableau interne value de la String, à partir de l'index offset, et contenant count caractères, vers le tableau de caractères dest.
Voilà la méthode de comparaison d'égalité. En premier lieu, si les références des deux objets à comparer sont les mêmes, elle retourne true.
Puis, on vérifie que anObject est une instance de String. Ceci grâce à l'opérateur instanceof, qui vérifie que la classe indiquée est une des classes(ou interface bien sûr) dont hérite l'objet, ou la classe de l'objet lui-même. Puis on vérifie que les deux chaînes sont de la même longueur. Si c'est le cas, il ne nous reste plus qu'à comparer les valeurs des deux tableaux internes de caractères, en commençant par leurs offsets respectifs.
On fait la boucle autant de fois que la longueur, et on compare chaque caractère. Dès qu'on trouve une différence, on retourne false. A la fin de la boucle, on retourne true.

Regardons à présent la méthode concat:
Si la longueur de la 2e chaîne = 0, on retourne la référence de la première chaîne.
On peut noter que le framework accepte de retourner une référence sur le même objet. Mais comme on ne risque pas, avec les String, de modifier le contenu de l'objet, ce n'est pas gênant d'avoir deux variables qui pointent sur la même référence.

Sinon, on peut créer un nouveau tableau de caractères pour la future String résultante, de longueur égale à la somme des deux longueurs. Puis on copie dans le nouveau tableau, à l'aide de this.getChars, les caractères de la première String. Et enfin, on copie le tableau de la deuxième chaîne, dans la seconde partie du nouveau tableau, avec la méthode getChars de la deuxième String.
Il ne reste plus qu'à créer un nouvel objet String, avec le constructeur qui sait prendre en paramètre notre nouveau tableau de caractères.

II-7) la méthode hashCode de String - Définition rapide du hashcode

Le hash code est une valeur entière, qui permet de nous aider à tester rapidement que deux objets(d'une même classe) sont égaux. Deux objets sont égaux, si leurs méthodes hashCode retourne le même entier, et ensuite on doit appeler leur méthode equals. Deux objets peuvent avoir le même hashcode, et les deux objets ne pas être égaux. Mais deux objets ne peuvent pas être égaux, si leur hashcode est différent.
Cela permet de comparer des objets plus rapidement. La classe doit implémenter une méthode int hashCode(), qui soit la plus appropriée possible, un compromis entre la rapidité de réponse, et la capacité à être précise du point de vue égalité. Par exemple, pour deux objets Personne, on peut imaginer une méthode hashCode qui sera une fonction des deux dates de naissance, et de la première lettre du nom de famille( c'est un exemple!).
Le hashcode est utilisé notamment dans des classes comme HashTable (java.util), pour comparer les clés. Dans ces classes, il est à noter que pour toutes les clés de la hash table, le hashcode est calculé une fois pour toutes à l'insertion de la paire clé/valeur. Ce qui signifie qu'à chaque recherche de clé, il n'y a que le hashcode de la clé à rechercher, qui est à calculer.
Il ne nous reste plus ensuite qu'à le comparer avec les hash codes déjà calculés.
La performance de la méthode hashCode() est donc moins importante ici; et sa précision présente plus d'intérêt( c'est à dire diminuer le nombre d'objets ayant le même hashcode), afin de diminuer les appels à equals pour les objets ayant le même hash code.
Note: en réalité, l'implémentation des hash tables est un peu plus complexe que cela. Car on profite du hash code pour en déduire(avec une valeur absolue, puis un modulo de la taille du tableau) un index dans un tableau de listes chaînées, qui sont chacune une liste chaînée de paires clé/valeur possibles(ça limite la recherche, mais toutes n'ont pas forcément le même hash code). Mais le principe reste tout-à-fait celui qu'on a énoncé.

Revenons à la classe String.

Voici la méthode hashCode() pour les String:
C'est donc une définition récursive du hashcode, écrite en itératif.
On calcule d'abord le premier hashcode, qui est le hashcode du premier caractère:
31*0 + code[premier_caract]. Puis on calcule le hashcode de la sous-chaine formée des deux premiers caractères: 31*hashcode_premier + code[deuxième_caractère] , et ainsi de suite.

RETOUR HAUT