Supposons qu'il y ait une chaîne, nous effectuerons de nombreuses opérations d'épissage de boucles sur cette chaîne, l'utilisation de "+" obtiendra les performances les plus basses. Mais à quel point cette performance est-elle mauvaise ? Quel serait le résultat si nous mettions également StringBuffer, StringBuilder ou String.concat() dans le test de performances ? Cet article apportera une réponse à ces questions !
Nous utiliserons Per4j pour calculer les performances, car cet outil peut nous fournir un ensemble complet d'indicateurs de performance, tels que le temps minimum et maximum pris, l'écart type de la période statistique, etc. Dans le code de test, afin d'obtenir une valeur d'écart type précise, nous effectuerons un test de 20 épissages "*" 50 000 fois. Voici la méthode que nous utiliserons pour concaténer des chaînes :
Copiez le code comme suit :
Opérateur de concaténation (+)
Méthode de concat de chaîne concat(String str)
Méthode d'ajout de StringBuffer append(String str)
Méthode d'ajout de StringBuilder append (String str)
Enfin, nous examinerons le bytecode pour voir comment exactement ces méthodes sont exécutées. Maintenant, commençons par créer notre classe. Notez que pour calculer les performances de chaque boucle, chaque code de test du code doit être encapsulé avec la bibliothèque Per4J. Nous définissons d'abord le nombre d'itérations
Copiez le code comme suit :
privé statique final int OUTER_ITERATION=20 ;
privé statique final int INNER_ITERATION=50000 ;
Ensuite, nous utiliserons les 4 méthodes ci-dessus pour implémenter notre code de test.
Copiez le code comme suit :
Chaîne addTestStr = "" ;
Chaîne concatTestStr = "" ;
StringBuffer concatTestSb = null;
StringBuilder concatTestSbu = null;
pour (int externalIndex=0;outerIndex<=OUTER_ITERATION;outerIndex++) {
StopWatch stopWatch = new LoggingStopWatch("StringAddConcat");
addTestStr = "";
pour (int innerIndex=0;innerIndex<=INNER_ITERATION;innerIndex++)
addTestStr += "*";
stopWatch.stop();
}
pour (int externalIndex=0;outerIndex<=OUTER_ITERATION;outerIndex++) {
StopWatch stopWatch = new LoggingStopWatch("StringConcat");
concatTestStr = "";
pour (int innerIndex=0;innerIndex<=INNER_ITERATION;innerIndex++)
concatTestStr.concat("*");
stopWatch.stop();
}
pour (int externalIndex=0;outerIndex<=OUTER_ITERATION;outerIndex++) {
StopWatch stopWatch = new LoggingStopWatch("StringBufferConcat");
concatTestSb = new StringBuffer();
pour (int innerIndex=0;innerIndex<=INNER_ITERATION;innerIndex++)
concatTestSb.append("*");
stopWatch.stop();
}
pour (int externalIndex=0;outerIndex<=OUTER_ITERATION;outerIndex++) {
StopWatch stopWatch = new LoggingStopWatch("StringBuilderConcat");
concatTestSbu = new StringBuilder();
pour (int innerIndex=0;innerIndex<=INNER_ITERATION;innerIndex++)
concatTestSbu.append("*");
stopWatch.stop();
}
Exécutez ensuite le programme pour générer des mesures de performances. Mon environnement d'exploitation est un système d'exploitation Windows 7 64 bits, une machine JVM 32 bits (7 unités) avec 4 Go de mémoire et un processeur Quad bicœur 2,00 GHz.
Cela s’est déroulé parfaitement comme nous l’avions imaginé. La seule chose intéressante est pourquoi String.concat est également très bon. Nous savons tous que String est une classe constante (une classe qui ne changera pas après l'initialisation), alors pourquoi les performances de concat sont-elles meilleures ? (Note du traducteur : en fait, il y a un problème avec le code de test de l'auteur original. Le code de test de la méthode concat() doit être écrit sous la forme concatTestStr=concatTestStr.concat("*").) Afin de répondre à cette question question, nous devrions examiner la décompilation concat. Le bytecode sort. Le package de téléchargement de cet article contient tous les bytecodes, mais jetons maintenant un œil à cet extrait de code de concat :
Copiez le code comme suit :
46 : nouveau #6 ; //classe java/lang/StringBuilder
49 : idiot
50 : invocationspecial #7; //Méthode java/lang/StringBuilder."<init>":()V
53 : aload_1
54 : invoquervirtual #8 ; //Méthode java/lang/StringBuilder.append :
(Ljava/lang/String;)Ljava/lang/StringBuilder;
57 : ldc #9 ; //Chaîne *
59 : invoquervirtual #8 ; //Méthode java/lang/StringBuilder.append :
(Ljava/lang/String;)Ljava/lang/StringBuilder;
62 : invoquervirtual #10 ; //Méthode java/lang/StringBuilder.toString :()
Ljava/lang/Chaîne ;
65 : astore_1
66 : iinc 7, 1
69 : aller à 38
Ce code est le bytecode de String.concat(). À partir de ce code, nous pouvons clairement voir que la méthode concat() utilise StringBuilder. Les performances de concat() devraient être aussi bonnes que celles de StringBuilder, mais en raison de la création supplémentaire de StringBuilder et. faire l'opération .append(str).append(str).toString() affectera les performances de la concaténation, donc StringBuilder et String Les temps de cancate sont de 1,8 et 3,3.
Par conséquent, même lors de la concaténation la plus simple, si nous ne voulons pas créer une instance StringBuffer ou StringBuilder, nous devons utiliser concat. Mais pour un grand nombre d'opérations d'épissage de chaînes, nous ne devons pas utiliser concat (Remarque du traducteur : étant donné que le code de test n'est pas complètement équivalent en fonction, le temps de traitement moyen de concat dans le code de test remplacé est de 1 650,9 millisecondes. Ce résultat est dans le commentaire texte original. ), car concat réduira les performances de votre programme et consommera votre CPU. Par conséquent, afin d'obtenir les performances les plus élevées sans prendre en compte la sécurité des threads et la synchronisation, nous devrions essayer d'utiliser StringBuilder.