J'étais occupé avec la mise en œuvre logique du projet pendant les jours de semaine. J'avais du temps samedi, alors j'ai sorti la version anglaise épaisse de Thinking In Java de la bibliothèque et j'ai lu sur l'épissage des objets chaîne. Faites une traduction en référence à ce livre, ajoutez vos propres réflexions et écrivez cet article pour l'enregistrer.
Objet String immuable
En Java, les objets String sont immuables. Dans le code, vous pouvez créer plusieurs alias pour un objet String. Mais ces alias font tous référence à la même chose.
Par exemple, s1 et s2 sont tous deux des alias de l'objet "droidyue.com", et les alias stockent des références aux objets réels. Donc s1 = s2
Copiez le code comme suit :
Chaîne s1 = "droidyue.com" ;
Chaîne s2 = s1 ;
System.out.println("s1 et s2 ont la même référence =" + (s1 == s2));
Le seul opérateur surchargé en Java
En Java, le seul opérateur surchargé est lié à la concaténation de chaînes. +,+=. De plus, les concepteurs Java n'autorisent pas la surcharge des autres opérateurs.
Analyse d'épissage
Y a-t-il vraiment un coût en termes de performances ?
Après avoir compris les deux points ci-dessus, vous pourriez avoir cette pensée. Puisque les objets Sting sont immuables, l'épissage de plusieurs chaînes (trois ou plus) produira inévitablement des objets String intermédiaires redondants.
Copiez le code comme suit :
Chaîne nomutilisateur = "Andy" ;
Âge de la chaîne = "24" ;
Travail de chaîne = "Développeur" ;
Informations sur la chaîne = nom d'utilisateur + âge + travail ;
Pour obtenir les informations ci-dessus, userName et age seront épissés pour générer un objet String temporaire t1, le contenu est Andy24, puis t1 et job seront épissés pour générer l'objet d'information final dont nous avons besoin. Parmi eux, un. l'intermédiaire t1 est généré, et t1 est créé. Ensuite, s'il n'y a pas de recyclage actif, il occupera inévitablement un certain espace. S'il s'agit d'un épissage de plusieurs chaînes (en supposant qu'il y en ait des centaines, principalement dans les appels à toString d'objets), le coût sera encore plus élevé et les performances seront considérablement réduites.
Traitement d'optimisation du compilateur
Y a-t-il vraiment un coût en termes de performances ? N'existe-t-il pas d'optimisation de traitement spéciale pour la concaténation de chaînes si couramment utilisée ? La réponse est oui. Cette optimisation est effectuée lorsque le compilateur compile .java en bytecode.
Si un programme Java veut s’exécuter, il doit passer par deux périodes : le temps de compilation et le temps d’exécution. Lors de la compilation, le compilateur Java (Compiler) convertit le fichier java en bytecode. Au moment de l'exécution, la machine virtuelle Java (JVM) exécute le bytecode généré au moment de la compilation. Au cours de ces deux périodes, Java a réalisé ce qu'on appelle la compilation en un seul endroit et s'est exécuté partout.
Expérimentons les optimisations qui ont été effectuées lors de la compilation et nous pourrons créer un morceau de code qui peut avoir une pénalité en termes de performances.
Copiez le code comme suit :
Concaténation de classe publique {
public static void main (String[] arguments) {
Chaîne nomutilisateur = "Andy" ;
Âge de la chaîne = "24" ;
Travail de chaîne = "Développeur" ;
Informations sur la chaîne = nom d'utilisateur + âge + travail ;
System.out.println(infos);
}
}
Compilez Concaténation.java. getConcaténation.class
Copiez le code comme suit :
javacConcaténation.java
Ensuite, nous utilisons javap pour décompiler le fichier Concatenation.class compilé. javap -c Concaténation. Si la commande javap n'est pas trouvée, pensez à ajouter le répertoire où se trouve javap à la variable d'environnement ou à utiliser le chemin complet vers javap.
Copiez le code comme suit :
17:22:04-androidyue~/workspace_adt/strings/src$ javap -c Concaténation
Compilé à partir de "Concaténation.java"
Concaténation de classe publique {
Concaténation publique ();
Code:
0 : aload_0
1 : invoque spécial #1 // Méthode java/lang/Object."<init>":()V
4 : retour
public static void main(java.lang.String[]);
Code:
0 : ldc #2 // Chaîne Andy
2 : astore_1
3 : ldc #3 // Chaîne 24
5 : astore_2
6 : ldc #4 // Développeur de chaînes
8 : astore_3
9 : nouveau #5 // classe java/lang/StringBuilder
12 : dupé
13 : Invocationspecial #6 // Méthode java/lang/StringBuilder."<init>":()V
16 : aload_1
17 : invoquervirtual #7 // Méthode java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
20 : aload_2
21 : invoquervirtual #7 // Méthode java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
24 : aload_3
25 : invoquervirtual #7 // Méthode java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
28 : invoquervirtual #8 // Méthode java/lang/StringBuilder.toString:()Ljava/lang/String;
31 : un magasin 4
33 : getstatic #9 // Champ java/lang/System.out:Ljava/io/PrintStream;
36 : charger 4
38 : invoquervirtual #10 // Méthode java/io/PrintStream.println:(Ljava/lang/String;)V
41 : retour
}
Parmi eux, ldc, astore, etc. sont des instructions de bytecode Java, similaires aux instructions d'assemblage. Les commentaires suivants utilisent du contenu lié à Java à des fins d'explication. Nous pouvons voir qu'il existe de nombreux StringBuilders ci-dessus, mais nous ne les appelons pas explicitement dans le code Java. Il s'agit de l'optimisation effectuée par le compilateur Java. Lorsque le compilateur Java rencontre un épissage de chaîne, il crée un objet StringBuilder, et ce qui suit. L'épissage appelle en fait la méthode append de l'objet StringBuilder. De cette façon, il n’y aura aucun problème dont nous nous inquiétions ci-dessus.
Optimisation du compilateur seule ?
Puisque le compilateur a effectué l'optimisation pour nous, est-il suffisant de se fier uniquement à l'optimisation du compilateur ? Bien sûr que non ?
Ci-dessous, nous examinons un morceau de code non optimisé avec des performances inférieures
Copiez le code comme suit :
public void implicitUseStringBuilder (valeurs String[]) {
Résultat de la chaîne = "" ;
pour (int i = 0; i < valeurs.longueur; i ++) {
résultat += valeurs[i] ;
}
System.out.println(résultat);
}
Utilisez javac pour compiler et javap pour afficher
Copiez le code comme suit :
public void implicitUseStringBuilder(java.lang.String[]);
Code:
0 : ldc #11 // Chaîne
2 : astore_2
3 : iconst_0
4 : istore_3
5 : iload_3
6 : aload_1
7 : longueur du tableau
8 : if_icmpge 38
11 : nouveau #5 // classe java/lang/StringBuilder
14 : dupé
15 : Invocationspecial #6 // Méthode java/lang/StringBuilder."<init>":()V
18 : aload_2
19 : invoquervirtual #7 // Méthode java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
22 : aload_1
23 : iload_3
24 : aaload
25 : invoquervirtual #7 // Méthode java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
28 : invoquervirtual #8 // Méthode java/lang/StringBuilder.toString:()Ljava/lang/String;
31 : astore_2
32 : iinc 3, 1
35 : aller à 5
38 : getstatic #9 // Champ java/lang/System.out:Ljava/io/PrintStream;
41 : aload_2
42 : invoquervirtual #10 // Méthode java/io/PrintStream.println:(Ljava/lang/String;)V
45 : retour
Parmi eux, 8 : if_icmpge 38 et 35 : goto 5 forment une boucle. 8 : if_icmpge 38 signifie que si la comparaison d'entiers de la pile d'opérandes JVM est supérieure ou égale à (le résultat opposé de i <values.length), passez à la ligne 38 (System.out). 35 : goto 5 signifie passer directement à la ligne 5.
Mais une chose très importante ici est que la création d'objets StringBuilder se produit entre les boucles, ce qui signifie combien d'objets StringBuilder seront créés dans autant de boucles, ce qui n'est évidemment pas bon. Code nu de bas niveau.
Un peu d'optimisation peut améliorer instantanément vos performances.
Copiez le code comme suit :
public void explicitUseStringBuider (String[] valeurs) {
Résultat StringBuilder = new StringBuilder();
pour (int i = 0; i < valeurs.longueur; i ++) {
result.append(values[i]);
}
}
Informations compilées correspondantes
Copiez le code comme suit :
public void explicitUseStringBuider(java.lang.String[]);
Code:
0 : nouveau #5 // classe java/lang/StringBuilder
3 : dupé
4: Invocationspecial #6 // Méthode java/lang/StringBuilder."<init>":()V
7 : astore_2
8 : iconst_0
9 : istore_3
10 : iload_3
11 : aload_1
12 : longueur du tableau
13 : if_icmpge 30
16 : aload_2
17 : aload_1
18 : iload_3
19 : aaload
20 : invoquervirtual #7 // Méthode java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
23 : pop
24 : iinc 3, 1
27 : aller à 10
30 : retour
Comme le montre ce qui précède, 13 : if_icmpge 30 et 27 : goto 10 forment une boucle et 0 : new #5 est en dehors de la boucle, donc StringBuilder ne sera pas créé plusieurs fois.
En général, nous devons essayer d'éviter de créer implicitement ou explicitement StringBuilder 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 peuvent écrire du code de niveau supérieur.
S'il y a des erreurs dans l'article ci-dessus, veuillez les critiquer et les corriger.