L'essentiel :
String
est une classe immuable, elle ne peut pas être modifiée. StringBuilder
est une classe mutable qui peut être ajoutée, remplacée ou supprimée par des caractères, et finalement convertie en une classe String
StringBuffer
est la version originale synchronisée de StringBuilder
Vous devriez préférer StringBuilder
dans tous les cas où un seul thread accède à votre objet.
Les détails :
Notez également que StringBuilder/Buffers
ne sont pas magiques, elles utilisent simplement un tableau comme objet de sauvegarde et ce tableau doit être réaffecté chaque fois qu'il est plein. Assurez-vous de créer votre StringBuilder/Buffer
des objets suffisamment grands à l'origine pour qu'ils ne doivent pas être constamment redimensionnés à chaque fois. .append()
est appelé.
Le redimensionnement peut devenir très dégénéré. En fait, il redimensionne le tableau de sauvegarde à deux fois sa taille actuelle chaque fois qu'il doit être étendu. Cela peut conduire à l'allocation de grandes quantités de RAM qui ne sont pas utilisées lorsque StringBuilder/Buffer
les classes commencent à se développer.
En Java String x = "A" + "B";
utilise un StringBuilder
dans les coulisses. Ainsi, pour les cas simples, il n'y a aucun avantage à déclarer les vôtres. Mais si vous construisez String
qui sont grands, disons moins de 4k, alors déclarer StringBuilder sb = StringBuilder(4096);
est beaucoup plus efficace que la concaténation ou l'utilisation de la fonction Constructeur par défaut qui ne comporte que 16 caractères. Si votre String
va être inférieure à 10k, alors initialisez-la avec le constructeur à 10k pour être sûr. Mais s'il est initialisé à 10k et que vous écrivez un caractère de plus que 10k, il sera réaffecté et copié dans un tableau de 20k. Il est donc préférable de l'initialiser à une valeur élevée plutôt qu'à une valeur faible.
Dans le cas de la redimensionnement automatique, au 17e caractère, le tableau de sauvegarde est réaffecté et copié sur 32 caractères, au 33e caractère, cela se produit à nouveau et vous devez réaffecter et copier le tableau sur 64 caractères. Vous pouvez voir comment cela dégénère en lots de réallocations et de copies, ce qui est ce que vous essayez vraiment d'éviter en utilisant StringBuilder/Buffer
en premier lieu.
Ceci est extrait du code source du JDK 6 pour AbstractStringBuilder.
void expandCapacity(int minimumCapacity) {
int newCapacity = (value.length + 1) * 2;
if (newCapacity < 0) {
newCapacity = Integer.MAX_VALUE;
} else if (minimumCapacity > newCapacity) {
newCapacity = minimumCapacity;
}
value = Arrays.copyOf(value, newCapacity);
}
Une bonne pratique consiste à initialiser le StringBuilder/Buffer
un peu plus grand que ce dont vous pensez avoir besoin si vous ne connaissez pas d'emblée la taille de l'appareil. String
sera mais vous pouvez le deviner. Une allocation d'un peu plus de mémoire que ce dont vous avez besoin est préférable à de nombreuses réallocations et copies.
Attention également à l'initialisation d'un StringBuilder/Buffer
avec un String
car cela n'allouera que la taille de la chaîne + 16 caractères, ce qui, dans la plupart des cas, ne fera que lancer le cycle dégénéré de réallocation et de copie que vous essayez d'éviter. Ce qui suit est tiré directement du code source de Java 6.
public StringBuilder(String str) {
super(str.length() + 16);
append(str);
}
Si par hasard vous vous retrouvez avec une instance de StringBuilder/Buffer
que vous n'avez pas créé et que vous ne pouvez pas contrôler le constructeur qui est appelé, il existe un moyen d'éviter le comportement dégénéré de réaffectation et de copie. Appelez .ensureCapacity()
avec la taille que vous voulez pour assurer que votre résultat String
s'adaptera.
Les alternatives :
Juste comme une note, si vous faites vraiment lourd String
et la manipulation, il existe une alternative beaucoup plus orientée vers les performances appelée Cordes .
Une autre solution consiste à créer un StringList
mise en œuvre par sous-classement ArrayList<String>
et l'ajout de compteurs pour suivre le nombre de caractères sur chaque page. .append()
et d'autres opérations de mutation de la liste, alors remplacez .toString()
pour créer un StringBuilder
de la taille exacte dont vous avez besoin, puis parcourir la liste en boucle et construire la sortie. StringBuilder
une variable d'instance et mettre en cache les résultats de l'opération. .toString()
et n'avoir à le générer à nouveau que lorsque quelque chose change.
N'oubliez pas non plus String.format()
lors de la construction d'une sortie formatée fixe, qui peut être optimisée par le compilateur au fur et à mesure qu'il l'améliore.