Maison  >  Article  >  Java  >  Analyse détaillée des performances des classes String, StringBuffer et StringBuilder en Java

Analyse détaillée des performances des classes String, StringBuffer et StringBuilder en Java

高洛峰
高洛峰original
2017-01-22 11:25:121389parcourir

Il faut d'abord rappeler les caractéristiques des trois :

Constante de chaîne String

Variable de chaîne StringBuffer (thread-safe)

Variable de chaîne StringBuilder (non-thread) Sécurité)

1. Définition
En regardant 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.

Chaîne : C'est une quantité immuable, c'est-à-dire qu'elle ne peut pas être modifiée après sa création.

StringBuffer : Il s'agit d'une séquence de chaînes variable. Comme String, elle stocke une séquence de chaînes ordonnées (tableau de type char) en mémoire. La différence est que la valeur de l'objet StringBuffer est variable.

StringBuilder : c'est fondamentalement la même chose que la classe StringBuffer. Ce sont à la fois des caractères variables et des séquences de chaînes. La différence est que StringBuffer est thread-safe et StringBuilder n'est pas sécurisé. En termes de performances, puisque le fonctionnement de la classe String consiste à générer de nouveaux objets String et que StringBuilder et StringBuffer ne sont que des extensions d'un tableau de caractères, le fonctionnement de la classe String est beaucoup plus lent que StringBuffer et StringBuilder.

2. Scénarios d'utilisation
Scénarios d'utilisation de la classe String : La classe String peut être utilisée dans des scénarios où les chaînes ne changent pas fréquemment, comme la déclaration de constantes et un petit nombre d'opérations variables.
Scénarios d'utilisation de la classe StringBuffer : si vous effectuez fréquemment des opérations sur les chaînes (telles que l'épissage, le remplacement, la suppression, etc.) et que vous les exécutez dans un environnement multithread, vous pouvez envisager d'utiliser StringBuffer, comme l'analyse XML, le paramètre HTTP. analyse et encapsulation.
Scénarios d'utilisation de la classe StringBuilder : si vous effectuez fréquemment des opérations sur les chaînes (telles que l'épissage, le remplacement et la suppression, etc.) et que vous les exécutez dans un environnement monothread, vous pouvez envisager d'utiliser StringBuilder, comme l'assemblage d'instructions SQL, Encapsulation JSON, etc.

3. Analyse
En bref, la principale différence de performances entre le type String et le type StringBuffer est que String est un objet immuable, donc chaque fois que le type String est modifié, cela équivaut en fait à générer Créez un nouvel objet String, puis pointez le pointeur vers le nouvel objet String. Par conséquent, il est préférable de ne pas utiliser String pour les chaînes dont le contenu change fréquemment, car chaque fois qu'un objet est généré, cela aura un impact sur les performances du système. Surtout lorsqu'il y a trop d'objets non référencés dans la mémoire, le GC de la JVM démarrera. pour travailler, et la vitesse sera certainement assez élevée.

Si vous utilisez la classe StringBuffer, le résultat sera différent. Chaque résultat fonctionnera sur l'objet StringBuffer lui-même, au lieu de générer un nouvel objet puis de changer la référence de l'objet. Donc, en général, nous recommandons d'utiliser StringBuffer, en particulier lorsque les objets chaîne changent fréquemment. Dans certains cas particuliers, la concaténation de chaînes d'objets String est en fait interprétée par la JVM comme la concaténation d'objets StringBuffer, donc dans ces cas, la vitesse des objets String ne sera pas plus lente que celle des objets StringBuffer, et en particulier les objets chaîne suivants sont Généré,String L'efficacité est beaucoup plus rapide que StringBuffer:

String S1 = “This is only a" + “ simple" + “ test";
StringBuffer Sb = new StringBuilder(“This is only a").append(“ simple").append(“ test");

Vous serez surpris de constater que la vitesse de génération de l'objet String S1 est tout simplement trop rapide, et à l'heure actuelle, StringBuffer n'a en réalité aucun avantage en termes de vitesse. En fait, c'est une astuce de la JVM. Aux yeux de la JVM, ce

String S1 = “This is only a" + “ simple" + “test";

est en fait :

String S1 = “This is only a simple test";
<.>

Alors bien sûr, cela ne prend pas beaucoup de temps. Mais ce que tout le monde doit noter ici, c'est que si votre chaîne provient d'un autre objet String, la vitesse ne sera pas si rapide, par exemple :

String S2 = "This is only a";
String S3 = "simple";
String S4 = "test";
String S1 = S2 +S3 + S4;

At cette fois, la JVM se comportera de la manière originale.

4. Traitement d'optimisation JVM approfondi

Y a-t-il vraiment un coût de performance ci-dessus ? L'épissage de chaînes est si couramment utilisé, n'y a-t-il pas d'optimisation de traitement spéciale La réponse est oui, cette optimisation est effectuée dans JVM ? compilation .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, Java JVM (Compilateur) convertit les fichiers 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 créons un morceau de code qui peut avoir une pénalité en termes de performances.

public class Concatenation {
 public static void main(String[] args) {
   String userName = "Andy";
   String age = "24";
   String job = "Developer";
   String info = userName + age + job;
   System.out.println(info);
 }
}

Compilez Concatenation.java. Obtenez Concatenation.class

javac Concatenation.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.

17:22:04-androidyue~/workspace_adt/strings/src$ javap -c Concatenation
Compiled from "Concatenation.java"
public class Concatenation {
 public Concatenation();
  Code:
    0: aload_0
    1: invokespecial #1         // Method java/lang/Object."<init>":()V
    4: return   
 
 public static void main(java.lang.String[]);
  Code:
    0: ldc      #2         // String Andy
    2: astore_1
    3: ldc      #3         // String 24
    5: astore_2
    6: ldc      #4         // String Developer
    8: astore_3
    9: new      #5         // class java/lang/StringBuilder
   12: dup
   13: invokespecial #6         // Method java/lang/StringBuilder."<init>":()V
   16: aload_1
   17: invokevirtual #7         // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
   20: aload_2
   21: invokevirtual #7         // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
   24: aload_3
   25: invokevirtual #7         // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
   28: invokevirtual #8         // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
   31: astore    4
   33: getstatic   #9         // Field java/lang/System.out:Ljava/io/PrintStream;
   36: aload     4
   38: invokevirtual #10         // Method java/io/PrintStream.println:(Ljava/lang/String;)V
   41: return
}

Parmi eux, ldc, astore, etc. se trouvent 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 JavaJVM. Lorsque la JavaJVM rencontre un épissage de chaîne, elle créera un objet StringBuilder. En fait, il s'agit d'appeler 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.

五、仅靠JVM优化?
既然JVM帮我们做了优化,是不是仅仅依靠JVM的优化就够了呢,当然不是。 
下面我们看一段未优化性能较低的代码

public void implicitUseStringBuilder(String[] values) {
 String result = "";
 for (int i = 0 ; i < values.length; i ++) {
   result += values[i];
 }
 System.out.println(result);
}

   

使用javac编译,使用javap查看

public void implicitUseStringBuilder(java.lang.String[]);
  Code:
    0: ldc      #11         // String
    2: astore_2
    3: iconst_0
    4: istore_3
    5: iload_3
    6: aload_1
    7: arraylength
    8: if_icmpge   38
   11: new      #5         // class java/lang/StringBuilder
   14: dup
   15: invokespecial #6         // Method java/lang/StringBuilder."<init>":()V
   18: aload_2
   19: invokevirtual #7         // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
   22: aload_1
   23: iload_3
   24: aaload
   25: invokevirtual #7         // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
   28: invokevirtual #8         // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
   31: astore_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         // Method java/io/PrintStream.println:(Ljava/lang/String;)V
   45: return

   

其中8: if_icmpge 38 和35: goto 5构成了一个循环。8: if_icmpge 38的意思是如果JVM操作数栈的整数对比大于等于(i 7db7972854f88856e0b26158d61ce2d3 String

Java.lang.StringBuffer是线程安全的可变字符序列。一个类似于 String 的字符串缓冲区,但不能修改。虽然在任意时间点上它都包含某种特定的字符序列,但通过某些方法调用可以改变该序列的长度和内容。在程序中可将字符串缓冲区安全地用于多线程。而且在必要时可以对这些方法进行同步,因此任意特定实例上的所有操作就好像是以串行顺序发生的,该顺序与所涉及的每个线程进行的方法调用顺序一致。

StringBuffer 上的主要操作是 append 和 insert 方法,可重载这些方法,以接受任意类型的数据。每个方法都能有效地将给定的数据转换成字符串,然后将该字符串的字符追加或插入到字符串缓冲区中。append 方法始终将这些字符添加到缓冲区的末端;而 insert 方法则在指定的点添加字符。

例如,如果 z 引用一个当前内容是“start”的字符串缓冲区对象,则此方法调用 z.append(“le”) 会使字符串缓冲区包含“startle”(累加);而 z.insert(4, “le”) 将更改字符串缓冲区,使之包含“starlet”。

在大部分情况下 StringBuilder > StringBuffer

java.lang.StringBuilder一个可变的字符序列是JAVA 5.0新增的。此类提供一个与 StringBuffer 兼容的 API,但不保证同步,所以使用场景是单线程。该类被设计用作 StringBuffer 的一个简易替换,用在字符串缓冲区被单个线程使用的时候(这种情况很普遍)。如果可能,建议优先采用该类,因为在大多数实现中,它比 StringBuffer 要快。两者的使用方法基本相同。

更多详细分析Java中String、StringBuffer、StringBuilder类的性能相关文章请关注PHP中文网!

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn