JavaRush /Blog Java /Random-FR /Chaînes en Java (classe java.lang.String)
Viacheslav
Niveau 3

Chaînes en Java (classe java.lang.String)

Publié dans le groupe Random-FR

Introduction

Le parcours d'un programmeur est un processus complexe et long. Et dans la plupart des cas, cela commence par un programme qui affiche Hello World à l'écran. Java ne fait pas exception (voir Leçon : L'application "Hello World!" ). Comme nous pouvons le voir, le message est généré en utilisant System.out.println("Hello World!"); Si vous regardez l'API Java, la méthode System.out.println prend String comme paramètre d'entrée . Ce type de données sera discuté.

Chaîne sous forme de séquence de caractères

En fait, String traduit de l’anglais est une chaîne. C'est vrai, le type String représente une chaîne de texte. Qu'est-ce qu'une chaîne de texte ? Une chaîne de texte est une sorte de séquence ordonnée de caractères qui se suivent. Le symbole est char. Séquence – séquence. Alors oui, tout à fait exact, String est une implémentation de java.lang.CharSequence. Et si vous regardez à l’intérieur de la classe String elle-même, alors à l’intérieur, il n’y a rien de plus qu’un tableau de caractères : private final char value[]; il a java.lang.CharSequenceun contrat assez simple :
Chaînes en Java (classe java.lang.String) - 1
Nous avons une méthode pour obtenir le nombre d'éléments, obtenir un élément spécifique et obtenir un ensemble d'éléments + la méthode toString elle-même, qui renverra ceci) Il est plus intéressant de comprendre les méthodes qui nous sont parvenues dans Java 8, et c'est : chars()et codePoints() rappelez-vous du didacticiel d'Oracle « Données primitives » Types " que char est single 16-bit Unicode character. Autrement dit, essentiellement, char n'est qu'un type deux fois moins grand qu'un int (32 bits) qui représente des nombres de 0 à 65535 (voir les valeurs décimales dans le tableau ASCII ). Autrement dit, si nous le souhaitons, nous pouvons représenter char comme int. Et Java 8 en a profité. À partir de la version 8 de Java, nous avons IntStream - un flux pour travailler avec des entiers primitifs. Par conséquent, dans charSequence, il est possible d'obtenir un IntStream représentant soit des caractères, soit des codePoints. Avant de passer à eux, nous verrons un exemple pour montrer la commodité de cette approche. Utilisons le compilateur Java en ligne Tutorialspoint et exécutons le code :
public static void main(String []args){
        String line = "aaabccdddc";
        System.out.println( line.chars().distinct().count() );
}
Vous pouvez désormais obtenir un certain nombre de symboles uniques de cette manière simple.

Points de code

Nous avons donc vu les caractères. Maintenant, on ne sait pas clairement de quel type de points de code il s’agit. Le concept de codePoint est apparu car à l'apparition de Java, 16 bits (un demi-entier) suffisaient pour encoder un caractère. Par conséquent, char en Java est représenté au format UTF-16 (spécification "Unicode 88"). Plus tard, est apparu Unicode 2.0, dont le concept était de représenter un caractère comme une paire de substitution (2 caractères). Cela nous a permis d'élargir la plage de valeurs possibles jusqu'à une valeur int. Pour plus de détails, consultez stackoverflow : " Comparer un caractère à un point de code ? " UTF-16 est également mentionné dans le JavaDoc pour Character . Là, dans le JavaDoc, il est dit que : Il In this representation, supplementary characters are represented as a pair of char values, the first from the high-surrogates range, (\uD800-\uDBFF), the second from the low-surrogates range (\uDC00-\uDFFF). est assez difficile (et peut-être même impossible) de reproduire cela dans des alphabets standards. Mais les symboles ne se limitent pas aux lettres et aux chiffres. Au Japon, ils ont inventé quelque chose d'aussi difficile à coder que l'emoji, le langage des idéogrammes et des émoticônes. Il existe un article intéressant à ce sujet sur Wikipédia : « Emoji ». Trouvons un exemple d'emoji, par exemple celui-ci : « Emoji Ghost ». Comme on peut le voir, le même codePoint y est même indiqué (valeur = U+1F47B). Il est indiqué au format hexadécimal. Si nous convertissons en nombre décimal, nous obtenons 128123. C'est plus que 16 bits autorisés (c'est-à-dire plus de 65535). Copieons-le :
Chaînes en Java (classe java.lang.String) - 2
Malheureusement, la plateforme JavaRush ne prend pas en charge ces caractères dans le texte. Par conséquent, dans l’exemple ci-dessous, vous devrez insérer une valeur dans String. Par conséquent, nous allons maintenant comprendre un test simple :
public static void main(String []args){
	    String emojiString = "Вставте сюда эмоджи через ctrl+v";
	    //На один emojiString приходится 2 чара (т.к. не влезает в 16 бит)
	    System.out.println(emojiString.codePoints().count()); //1
	    System.out.println(emojiString.chars().count()); //2
}
Comme vous pouvez le voir, dans ce cas, 1 codePoint vaut 2 caractères. C'est la magie.

Personnage

Comme nous l'avons vu ci-dessus, les chaînes en Java sont constituées de caractères. Un type primitif vous permet de stocker une valeur, mais un wrapper java.lang.Charactersur un type primitif vous permet de faire beaucoup de choses utiles avec ce symbole. Par exemple, nous pouvons convertir une chaîne en majuscule :
public static void main(String[] args) {
    String line = "организация объединённых наций";
    char[] chars = line.toCharArray();
    for (int i = 0; i < chars.length; i++) {
        if (i == 0 || chars[i - 1] == ' ') {
            chars[i] = Character.toUpperCase(chars[i]);
        }
    }
    System.out.println(new String(chars));
}
Eh bien, diverses choses intéressantes : isAlphabetic(), isLetter(), isSpaceChar(), isDigit(), isUpperCase(), isMirrored()(par exemple, des crochets. '(' a une image miroir ')').

Piscine à cordes

Les chaînes en Java sont immuables, c'est-à-dire constantes. Ceci est également indiqué dans le JavaDoc de la classe java.lang.String elle-même . Deuxièmement, et c'est également très important, les chaînes peuvent être spécifiées sous forme de littéraux :
String literalString = "Hello, World!";
String literalString = "Hello, World!";
Autrement dit, toute chaîne entre guillemets, comme indiqué ci-dessus, est en réalité un objet. Et cela soulève la question : si nous utilisons des chaînes si souvent et qu'elles peuvent souvent être les mêmes (par exemple, le texte « Erreur » ou « Avec succès »), existe-t-il un moyen de s'assurer que les chaînes ne sont pas créées à chaque fois ? À propos, nous avons toujours Maps, où la clé peut être une chaîne. Ensuite, nous ne pouvons certainement pas avoir les mêmes chaînes comme objets différents, sinon nous ne pourrons pas obtenir l'objet de la carte. Les développeurs Java ont réfléchi, réfléchi et ont créé String Pool. Il s'agit d'un endroit où les chaînes sont stockées, vous pouvez l'appeler un cache de chaînes. Toutes les lignes elles-mêmes n'y aboutissent pas, mais uniquement les lignes spécifiées dans le code par un littéral. Vous pouvez ajouter vous-même une ligne au pool, mais nous en reparlerons plus tard. Donc, en mémoire, nous avons ce cache quelque part. Une bonne question : où se trouve cette piscine ? La réponse à cette question peut être trouvée sur stackoverflow : « Où réside le pool de constantes String de Java, le tas ou la pile ? " Il est situé dans la mémoire Heap, dans une zone spéciale de pool de constantes d'exécution. Le pool de constantes d'exécution est alloué lorsqu'une classe ou une interface est créée par la machine virtuelle à partir de la zone de méthode - une zone spéciale du tas à laquelle tous les threads de la machine virtuelle Java ont accès. Que nous apporte le String pool ? Cela présente plusieurs avantages :
  • Les objets du même type ne seront pas créés
  • La comparaison par référence est plus rapide que la comparaison caractère par caractère via des égalités
Mais que se passe-t-il si nous voulons mettre l’objet créé dans ce cache ? Ensuite, nous avons une méthode spéciale : String.intern Cette méthode ajoute une chaîne au String Pool. Il convient de noter qu'il ne s'agit pas simplement d'une sorte de cache sous la forme d'un tableau (comme pour les entiers). La méthode interne est spécifiée comme « native ». Cela signifie que la méthode elle-même est implémentée dans un autre langage (principalement C++). Dans le cas des méthodes Java de base, diverses autres optimisations peuvent leur être appliquées au niveau JVM. En général, la magie opère ici. Il est intéressant de lire l'article suivant sur les stagiaires : https://habr.com/post/79913/#comment_2345814 Et cela semble être une bonne idée. Mais comment cela va-t-il nous affecter ? Mais ça aura vraiment un impact)
public static void main(String[] args) {
    String test = "literal";
    String test2 = new String("literal");
    System.out.println(test == test2);
}
Comme vous pouvez le constater, les lignes sont les mêmes, mais le résultat sera faux. Et tout cela parce que == ne compare pas par valeur, mais par référence. Et voici comment cela fonctionne :
public static void main(String[] args) {
    String test = "literal";
    String test2 = new String("literal").intern();
    System.out.println(test == test2);
}
Notez simplement que nous créerons toujours de nouvelles String. Autrement dit, stagiaire nous renverra une chaîne du cache, mais la chaîne d'origine que nous avons recherchée dans le cache sera rejetée pour le nettoyage, car personne d'autre ne le connaît. Il s'agit clairement d'une consommation inutile de ressources =( Par conséquent, vous devez toujours comparer les chaînes en utilisant des valeurs égales afin d'éviter autant que possible les erreurs soudaines et difficiles à détecter.
public static void main(String[] args) {
    String test = "literal";
    String test2 = new String("literal").intern();
    System.out.println(test.equals(test2));
}
Equals effectue une comparaison de chaînes caractère par caractère.

Enchaînement

On s'en souvient, des lignes peuvent être ajoutées. Et comme nous le rappelons, nos cordes sont immuables. Alors, comment ça marche ? C'est vrai, une nouvelle ligne est créée, composée de symboles des objets ajoutés. Il existe un million de versions du fonctionnement de la concaténation Plus. Certains pensent qu’il y aura un nouvel objet à chaque fois, d’autres pensent qu’il y aura autre chose. Mais une seule personne peut avoir raison. Et ce quelqu'un est le compilateur javac. Utilisons le service de compilateur en ligne et exécutons :
public class HelloWorld {

    public static void main(String[] args) {
        String helloMessage = "Hello, ";
        String target = "World";
        System.out.println(helloMessage + target);
    }

}
Maintenant, sauvegardons cela sous forme d'archive zip, extrayons-le dans un répertoire et exécutons : javap –c HelloWorld Et ici nous découvrons tout :
Chaînes en Java (classe java.lang.String) - 3
En boucle, bien sûr, il est préférable de faire soi-même la concaténation via StringBuilder. Et pas à cause d'une sorte de magie, mais pour que StringBuilder soit créé avant le cycle et que seul l'ajout se produise dans le cycle lui-même. À propos, il y a une autre chose intéressante ici. Il existe un excellent article : « String Processing in Java. Partie I : String, StringBuffer, StringBuilder ." Beaucoup d'informations utiles dans les commentaires. Par exemple, il est précisé que lors de la concaténation d'une vue, new StringBuilder().append()...toString()une optimisation intrinsèque est en vigueur, régulée par l'option -XX:+OptimizeStringConcat, qui est activée par défaut. intrinsèque - traduit par « interne ». La JVM gère ces choses d'une manière spéciale, en les traitant comme natifs, mais sans les coûts supplémentaires de JNI. Lire la suite : « Méthodes intrinsèques dans HotSpot VM ».

StringBuilder et StringBuffer

Comme nous l'avons vu ci-dessus, StringBuilder est un outil très utile. Les chaînes sont immuables, c'est-à-dire immuable. Et je veux le plier. Par conséquent, nous disposons de 2 classes pour nous aider : StringBuilder et StringBuffer. La principale différence entre les deux est que StringBuffer a été introduit dans JDK 1.0, tandis que StringBuilder est arrivé dans Java 1.5 en tant que version non synchronisée de StringBuffer pour éliminer la surcharge liée à la synchronisation inutile des méthodes. Ces deux classes sont des implémentations de la classe abstraite AbstractStringBuilder - Une séquence de caractères mutable. Un tableau de charmes est stocké à l'intérieur, qui est développé selon la règle : value.length * 2 + 2. Par défaut, la taille (capacité) de StringBuilder est de 16.

Comparable

Les chaînes sont comparables, c'est-à-dire implémentez la méthode compareTo. Cela se fait en utilisant une comparaison caractère par caractère. Fait intéressant, la longueur minimale est sélectionnée parmi deux chaînes et une boucle est exécutée dessus. Par conséquent, compareTo renverra soit la différence entre les valeurs int des premiers caractères sans correspondance jusqu'à la plus petite longueur de chaîne, soit la différence entre les longueurs de chaîne si tous les caractères correspondent dans la longueur minimale de chaîne. Cette comparaison est dite « lexicographique ».

Travailler avec des chaînes Java

String a de nombreuses méthodes utiles :
Chaînes en Java (classe java.lang.String) - 4
Il existe de nombreuses tâches pour travailler avec des chaînes. Par exemple, sur Coding Bat . Il existe également un cours sur coursera : « Algorithmes sur chaînes ».

Conclusion

Même un bref aperçu de cette classe occupe un espace impressionnant. Et ce n'est pas tout. Je recommande fortement de regarder le rapport de JPoint 2015 : Alexey Shipilev - Catéchisme java.lang.String
#Viacheslav
Commentaires
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION