Nous devons d'abord nous souvenir des caractéristiques de trois:
1. Définition
Lorsque vous regardez l'API, vous constaterez que String, StringBuffer et StringBuilder implémentent tous l'interface CharSequence. Bien qu'ils soient tous liés aux chaînes, leurs mécanismes de traitement sont différents.
2. Utilisez des scénarios
Scénarios utilisant la classe String: Dans les scénarios où les chaînes ne changent pas fréquemment, les classes de chaînes peuvent être utilisées, telles que les déclarations de constantes et le petit nombre d'opérations variables.
Scénarios utilisant la classe StringBuffer: lorsque les opérations de chaîne sont fréquemment effectuées (comme l'épissage, la substitution, la suppression, etc.) et l'exécution dans un environnement multithread, vous pouvez envisager d'utiliser StringBuffer, tels que l'analyse XML, l'analyse des paramètres HTTP et l'encapsulation.
Scénarios utilisant StringBuilder Classe: Lorsque les opérations de chaîne sont fréquemment effectuées (comme l'épissage, la substitution et la suppression), et exécuter dans un environnement unique, vous pouvez envisager d'utiliser StringBuilder, tel que l'assemblage de l'instruction SQL, l'encapsulation JSON, etc.
Iii. Analyse
Pour le dire simplement, la différence de performance principale entre le type de chaîne et le type de stringbuffer est en fait que String est un objet immuable, donc chaque fois que vous modifiez le type de chaîne, il est en fait équivalent à la génération d'un nouvel objet String puis à pointer le pointeur vers le nouvel objet String. Par conséquent, il est préférable de ne pas utiliser la chaîne pour les chaînes qui modifient fréquemment du contenu, car chaque génération d'objets aura un impact sur les performances du système, surtout lorsqu'il y a trop d'objets référencés en mémoire, le GC du JVM commencera à fonctionner, ce qui sera certainement assez lent.
Si vous utilisez la classe StringBuffer, le résultat sera différent. Chaque fois que le résultat fonctionne sur l'objet StringBuffer lui-même, au lieu de générer un nouvel objet, puis de modifier la référence d'objet. Par conséquent, en général, nous recommandons d'utiliser StringBuffer, en particulier lorsque les objets de chaîne sont souvent modifiés. Dans certains cas particuliers, l'épissage de chaîne des objets String est en fait interprété par le JVM comme une épissage d'objets StringBuffer. Par conséquent, la vitesse des objets String n'est pas plus lente que les objets StringBuffer à ces moments. Surtout dans la génération d'objets de chaîne suivants, l'efficacité de la chaîne est beaucoup plus rapide que StringBuffer:
String s1 = "Ceci n'est qu'à un" + "simple" + "test"; stringBuffer sb = new StringBuilder ("Ceci n'est qu'à un").Vous serez surpris de constater que la vitesse de génération d'objets S1 String est tout simplement trop rapide, et à ce moment, StringBuffer n'a aucun avantage en vitesse. En fait, c'est une astuce de la JVM. Aux yeux du JVM, ce
String S1 = "Ceci n'est qu'un" + "simple" + "test";
En fait:
String S1 = "Ce n'est qu'un simple test";
Donc, bien sûr, cela ne prend pas beaucoup de temps. Mais ce que vous devez noter ici, c'est que si votre chaîne provient d'un autre objet de chaîne, la vitesse n'est pas si rapide, par exemple:
String S2 = "Ceci est seulement un"; String S3 = "Simple"; String S4 = "Test"; String S1 = S2 + S3 + S4;
À l'heure actuelle, le JVM le fera régulièrement de la manière originale.
4. Optimisation et traitement approfondie de JVM
Y a-t-il vraiment le coût de performance ci-dessus? L'épissage des cordes est si courant, n'y a-t-il pas d'optimisation spéciale de traitement? La réponse est que cette optimisation est effectuée lors de la compilation de .java en bytecode dans JVM.
Si un programme Java veut fonctionner, il faudra deux périodes, compilera le temps et le temps d'exécution. Au moment de la compilation, le Java JVM (compilateur) convertit le fichier Java en bytecode. Au moment de l'exécution, la machine virtuelle Java (JVM) exécute le bytecode généré par compilation. Pendant ces deux périodes, Java a atteint la soi-disant compilation et courir partout.
Expérimentons des optimisations faites pendant la période de compilation, et nous créons un morceau de code qui peut avoir des coûts de performance.
classe publique ConcaTENation {public static void main (String [] args) {String username = "Andy"; String Age = "24"; String Job = "développeur"; Chaîne info = nom d'utilisateur + âge + travail; System.out.println (info); }}Compiler la concaténation.java. Obtenir la concaténation.classe
javac concatenation.java
Ensuite, nous utilisons Javap pour décompiler le fichier compilé Concaténation.Class. Concaténation Javap -C. Si la commande Javap n'est pas trouvée, veuillez envisager d'ajouter le répertoire où le Javap est situé à la variable d'environnement ou en utilisant le chemin complet de Javap.
17: 22: 04-androidyue ~ / workspace_adt / strings / src $ javap -c concaténationcompilé de "concatenation.java" classe publique Concaténation {public concatenation (); Code: 0: aload_0 1: invokeSpecial # 1 // Méthode java / lang / objet. "<Init>" :() v 4: return public static void main (java.lang.string []); CODE: 0: LDC # 2 // String Andy 2: Store_1 3: LDC # 3 // String 24 5: Store_2 6: LDC # 4 // STRING Developer 8: Store_3 9: Nouveau # 5 // Classe Java / Lang / StringBuilder 12: Dup 13: InvokEspecial # 6 // Méthode Java / Lang / StringBuilder. invokeVirtual # 7 // Méthode java / lang / stringBuilder.append: (ljava / lang / string;) ljava / lang / stringBuilder; 20: aload_2 21: invokevirtual # 7 // méthode java / lang / stringBuilder.append: (ljava / lang / string;) ljava / lang / stringBuilder; 24: aload_3 25: invokevirtual # 7 // Méthode java / lang / stringBuilder.append: (ljava / lang / string;) ljava / lang / stringBuilder; 28: invokeVirtual # 8 // Méthode java / lang / stringBuilder.tostring :() ljava / lang / string; 31: Store 4 33: GetStatic # 9 // Field Java / Lang / System.out: ljava / io / printStream; 36: ALOAD 4 38: invokevirtual # 10 // méthode java / io / printstream.println: (ljava / lang / string;) v 41: return}Parmi eux, les LDC, les magasins, etc. sont des instructions Java Bytecode, similaires aux instructions d'assemblage. Les commentaires suivants utilisent du contenu lié à Java pour l'explication. Nous pouvons voir qu'il y a de nombreux Builders de String, mais nous les appelons dans le code Java sans affichage. Il s'agit de l'optimisation faite par JavaJVM. Lorsque javajvm rencontre un épissage de chaîne, un objet StringBuilder sera créé. L'épissage derrière elle appelle réellement la méthode d'ajout de l'objet StringBuilder. De cette façon, il n'y aura aucun problème qui nous inquiète.
5. Vous vous fiez uniquement à l'optimisation JVM?
Puisque le JVM nous a aidés à optimiser, est-il suffisant pour s'appuyer uniquement sur l'optimisation JVM? Bien sûr que non.
Regardons un morceau de code qui n'a pas été optimisé pour de faibles performances
public void implicitUSestRingBuilder (String [] valeurs) {String result = ""; for (int i = 0; i <valeurs.length; i ++) {result + = valeurs [i]; } System.out.println (résultat);}Compiler avec Javac et voir avec Javap
public void implicitUSestringBuilder (java.lang.string []); Code: 0: LDC # 11 // String 2: Store_2 3: iconst_0 4: istore_3 5: iload_3 6: Aload_1 7: ArrayLength 8: if_icmpge 38 11: Nouveau # 5 // Classe Java / Lang / StringBuilder 14: Dup 15: Invoquepecial # 6 // Méthode java / lang / stringBuilder. "<init>" :() v 18: aload_2 19: invokevirtual # 7 // méthode java / lang / stringbuilder.append: (ljava / lang / string;) ljava / lang / stringBuilder; 22: aload_1 23: Iload_3 24: aaload 25: invokevirtual # 7 // méthode java / lang / stringBuilder.append: (ljava / lang / string;) ljava / lang / stringBuilder; 28: invokeVirtual # 8 // Méthode java / lang / stringBuilder.tostring :() ljava / lang / string; 31: Store_2 32: IINC 3, 1 35: Goto 5 38: GetStatic # 9 // Field Java / Lang / System.out: Ljava / Io / PrintStream; 41: aload_2 42: invokevirtual # 10 // méthode java / io / printstream.println: (ljava / lang / string;) v 45: return
Parmi eux 8: if_icmpge 38 et 35: goto 5 forment une boucle. 8: IF_ICMPGE 38 signifie que si la comparaison entière de la pile d'opérande JVM est supérieure ou égale à (i <le résultat inverse de VALEURS.Length), il passera à la ligne 38 (System.out). 35: Goto 5 signifie sauter directement à la ligne 5.
Mais il y a une chose très importante ici que la création d'objets StringBuilder se produit entre les boucles, ce qui signifie que le nombre de fois que les boucles créeront plusieurs objets StringBuilder, ce qui n'est évidemment pas bon. Code de bas niveau nu.
Optimisez-le légèrement pour améliorer instantanément la qualité.
public void expliciteStringBuilder (string [] valeurs) {stringBuilder result = new StringBuilder (); for (int i = 0; i <valeurs.length; i ++) {result.append (valeurs [i]); }}Informations compilées correspondantes
11: aload_1 12: arrayLength 13: if_icmpge 30 16: aload_2 17: aload_1 18: iload_3 19: aaload 20: invokevirtual # 7 // méthode java / lang / stringbuilder.append: (ljava / lang / string;) ljava / lang / stringbuilder; 23: Pop 24: Iinc 3, 1 27: Goto 10 30: Retour
Comme on peut le voir d'en haut, 13: if_icmpge 30 et 27: GOTO 10 Formez une boucle de boucle, tandis que 0: New # 5 est en dehors de la boucle, donc StringBuilder n'est pas créé plusieurs fois.
En général, nous devons essayer d'éviter la création implicite ou explicite de Builders dans le corps de la boucle. Par conséquent, ceux qui comprennent comment le code est compilé et comment il est exécuté en interne a des niveaux de code relativement élevés.
6. Conclusion
Dans la plupart des cas, StringBuffer> String
Java.lang.stringbuffer est une séquence de filetage de caractères variables. Un tampon de chaîne de type chaîne, mais ne peut pas être modifié. Bien qu'il contient une certaine séquence de caractères à tout moment, la longueur et le contenu de la séquence peuvent être modifiés par certains appels de méthode. Les tampons de chaînes peuvent être utilisés en toute sécurité dans les programmes de multithreading. Et ces méthodes peuvent être synchronisées lorsque cela est nécessaire, de sorte que toutes les opérations sur une instance particulière semblent se produire dans un ordre de série cohérent avec l'ordre des appels de méthode effectués par chaque thread impliqué.
Les principales opérations sur StringBuffer sont les méthodes d'ajout et d'insertion, qui peuvent être surchargées pour accepter tout type de données. Chaque méthode peut convertir efficacement les données données en une chaîne, puis ajouter ou insérer les caractères de cette chaîne en tampon de chaîne. La méthode d'ajout ajoute toujours ces caractères à la fin du tampon; La méthode d'insert ajoute des caractères au point spécifié.
Par exemple, si Z fait référence à un objet tampon de chaîne dont le contenu actuel est "Démarrer", cette méthode appelle z.append ("le") fera que le tampon de chaîne contienne "Starking" (accumulé); et z.insert (4, "le") modifiera le tampon de chaîne pour contenir "Starlet".
Dans la plupart des cas, StringBuilder> StringBuffer
java.lang.stringBuilder Une séquence de caractères variable est nouvelle dans Java 5.0. Cette classe fournit une API compatible StringBuffer, mais n'est pas garantie d'être synchronisée, donc le scénario d'utilisation est unique. Cette classe est conçue pour être un simple remplacement pour StringBuffer, lorsque des tampons de chaîne sont utilisés par un seul thread (c'est courant). Si possible, il est recommandé de suivre ce cours en premier, car il est plus rapide que StringBuffer dans la plupart des implémentations. Les méthodes d'utilisation des deux sont fondamentalement les mêmes.