StringBuilder-réinitialiser ou créer un nouveau
J'ai une condition qu'un StringBuilder conserve le stockage des lignes correspondant à un motif à partir d'un gros fichier plat (100 de Mo). Cependant, après avoir atteint une condition, j'écris le contenu de StringBuilder varialble dans un fichier texte.
Maintenant, je me demande si je devrais utiliser la même variable en réinitialisant l'objet - >
stringBuilder.delete(0,stringBuilder.length())
Ou
stringBuilder=new StringBuilder();
Veuillez suggérer ce qui vous semble le mieux en ce qui concerne les problèmes de performance et de MOO.
8 réponses
Je pense que StringBuilder#delete(start, end)
est toujours appel cher, vous devriez faire:
stringBuilder.setLength(0);
Pour le réinitialiser.
Mise à JOUR: Après avoir regardé le code source de StringBuilder
Il semble setLength(int)
feuilles vieux tampon intacte et il est préférable d'appeler: StringBuilder#trimToSize()
après l'appel ci-dessus qui attempts to reduce storage used for the character sequence
.
Donc quelque chose comme ça serait plus efficace:
stringBuilder.setLength(0); // set length of buffer to 0
stringBuilder.trimToSize(); // trim the underlying buffer
Imho, je suggère d'utiliser new:
stringBuilder = new StringBuilder();
Je n'ai jamais entendu parler d'une fuite de mémoire dans StringBuilder, mais quand vous repoussez vraiment les limites, vous ne savez jamais. Je couvrirais Mes paris contre elle en utilisant une nouvelle instance à chaque fois.
Dans le pire des cas, peut-être que vous perdez de l'efficacité et que le gc obtient un entraînement, mais vous excluez la possibilité de MOO.
Pour cette raison, et aussi pour des raisons de clarté, j'adopterais personnellement la nouvelle approche.
Une différence fondamentale est sb.delete conserve la référence, tandis que le constructeur la perd.
Si votre SB est un argument de méthode et est censé être utilisé pour renvoyer le contenu à l'appelant, vous devez utiliser sb.supprimer. L'appelant est titulaire de la référence d'origine.
Eh bien, il y a une plus grande différence entre les deux. Le premier conserve la capacité qu'il avait avant de supprimer les caractères (c'est-à-dire stringBuilder.capacity()
) tandis que le second crée un Nouveau StringBuilder
avec la capacité par défaut, 16. Bien sûr, vous pouvez simplement passer stringBuilder.capacity()
comme argument au constructeur, mais il est important de comprendre la distinction ici, néanmoins.
Dans tous les cas, je doute fortement que vous verrez une différence de performance substantielle entre ces deux variantes, alors choisissez ce qui est plus lisible et plus facile à gérer. seulement lorsque vous avez déterminé de manière concluante que cela provoque une sorte de goulot d'étranglement si vous changez votre approche.
Je voudrais utiliser:
stringBuilder = new StringBuilder();
Parce que si vous le remplissez avec une grande quantité de données, l'appel stringBuilder.setLength(0);
ne désallouera pas le tableau de sauvegarde, de sorte que vous pourriez voir l'utilisation de la mémoire rester élevée inutilement.
En outre, c'est juste plus facile à lire et à comprendre.
Idéalement, nous devrions utiliser new StringBuilder()
En creusant un peu dans la classe StringBuilder de grepcode j'apprends à connaître ce qui suit.
La Création d'un nouvel objet :
/**
* Creates an AbstractStringBuilder of the specified capacity.
*/
AbstractStringBuilder(int capacity) {
value = new char[capacity];
}
New StringBuilder () crée un nouvel objet avec un tableau de char de capacité initiale. Overhead ici: GC sera appelé pour effacer l'objet plus ancien.
L'Aide de la suppression :
public AbstractStringBuilder delete(int start, int end) {
if (start < 0)
throw new StringIndexOutOfBoundsException(start);
if (end > count)
end = count;
if (start > end)
throw new StringIndexOutOfBoundsException();
int len = end - start;
if (len > 0) {
System.arraycopy(value, start+len, value, start, count-end);
count -= len;
}
return this;
}
En utilisant Length et TrimToSize:
public void trimToSize() {
if (count < value.length) {
value = Arrays.copyOf(value, count);
}
}
Appellera copyOf à partir de la classe array
Public statique char[] copyOf(char[] original, int nouvellelongueur) { char[] copie = new char[nouvellelongueur]; Système.arraycopy (original, 0, copie, 0, Mathématique.min(d'origine.longueur, nouvellelongueur)); exemplaire de retour; }
Maintenant, il appellera également le système .arrayCopy qui est une méthode native. Maintenant si vous voyez dans copyOf nous créons à nouveau un nouveau charArray de longueur 0, et quand nous essayons d'ajouter à nouveau des données, il appellera expand car la longueur actuelle serait 0. J'Ai Donc pense qu'il vaut mieux appeler new StringBuilder ()
, Vous pouvez voir le code ci-dessus sur grepcode
PS: @user3241961 est écrit au cas où vous utiliseriez la référence de cet objet alors new nécessiterait de le définir à nouveau
Il est moins cher de réutiliser l'objet créé que d'en allouer un nouveau, toujours. Cela évite également un travail supplémentaire pour le garbage Collector, car vous ne manipulez qu'un objet.
Le moyen le plus rapide est:
stringBuilder.setLength(0);
Si vous êtes dans une boucle serrée et que vous continuerez dans cette boucle après avoir écrit les données dans le fichier, vous devriez certainement réutiliser le StringBuilder. Il n'y a aucune raison de ne pas le faire et c'est mieux que de baratter le GC. Si vous écriviez ceci en C ou C++ , vous réutiliseriez le tampon.
Aussi, bien que vrai que la suppression(...) système d'appels de méthode.arraycopy, le nombre d'octets copiés est 0 donc c'est insignifiant.
Ah-quelqu'un d'autre m'a mentionné qu'il y a un setLength (...) méthode qui est le moyen le plus rapide de réutiliser le tampon.