Effacer un tampon / générateur de chaîne après une boucle

122

Comment effacer le tampon de chaîne en Java après une boucle afin que la prochaine itération utilise un tampon de chaîne clair?

cascade
la source
2
Autre question: pourquoi n'utilisez-vous un SB que pour une seule itération de la boucle? Y a-t-il une autre itération intérieure? SB ne vaut pas la peine si vous voulez simplement faire A + B + C + D (le compilateur Java utilisera un SB en interne). Cela aide si vous voulez ajouter conditionnellement des parties de chaînes, mais sinon ... utilisez simplement "+".
helios

Réponses:

135

Une option consiste à utiliser la méthode de suppression comme suit:

StringBuffer sb = new StringBuffer();
for (int n = 0; n < 10; n++) {
   sb.append("a");

   // This will clear the buffer
   sb.delete(0, sb.length());
}

Une autre option (nettoyeur de bits) utilise setLength (int len) :

sb.setLength(0);

Voir Javadoc pour plus d'informations:

Jon
la source
15
Quelque chose d'un peu moins stupide serait de simplement déclarer le StringBuffer dans la boucle.
Mark Elliot
11
Ah, je pense que sb.setLength (0); est plus propre et plus efficace que de le déclarer à l'intérieur de la boucle. Votre solution va à l'encontre de l'avantage de performance de l'utilisation de StringBuffer ...
Jon
6
Je pense que l'avantage en termes de performances vient de la mutabilité de la chaîne, pas de la sauvegarde de l'instanciation. voici un test rapide des itérations 1e8: boucle intérieure (2,97s): ideone.com/uyyTL14w , boucle extérieure (2,87s): ideone.com/F9lgsIxh
Mark Elliot
6
En parlant de performances: à moins que votre code ne soit accessible dans un scénario multithread, vous devriez utiliser StringBuilder plutôt que StringBuffer - voir javadoc: "Dans la mesure du possible, il est recommandé d'utiliser cette classe de préférence à StringBuffer car elle sera plus rapide sous la plupart des implémentations ".
Rahel Lüthy
4
Le seul avantage de créer le SB à l'extérieur est de ne pas perdre le caractère interne (potentiellement long) [] de celui-ci. Si dans le premier itérateur il a suffisamment grandi, la deuxième boucle n'aura pas besoin de redimensionner un char []. Mais pour obtenir l'avantage, la "méthode claire" devra conserver la taille du tableau interne. setLength fait cela mais il met également à \ u0000 tous les caractères non utilisés dans le SB, donc il est moins performant que de simplement créer un nouveau SB avec une bonne capacité initiale. Il est préférable de déclarer à l'intérieur de la boucle.
helios
48

Le moyen le plus simple de réutiliser le StringBufferest d'utiliser la méthodesetLength()

public void setLength(int newLength)

Vous pouvez avoir le cas comme

StringBuffer sb = new StringBuffer("HelloWorld");
// after many iterations and manipulations
sb.setLength(0);
// reuse sb
Mossaddeque Mahmood
la source
2
@MMahmoud, Dans l'exemple de code, il devrait lire à la setLengthplace de setlength.
OnaBai
Quand je vois la source du code setLength, comment ça marche ( gist.github.com/ebuildy/e91ac6af2ff8a6b1821d18abf2f8c9e1 ) ici, le nombre sera toujours supérieur à newLength (0)?
Thomas Decaux
20

Vous avez deux options:

Soit utiliser:

sb.setLength(0);  // It will just discard the previous data, which will be garbage collected later.  

Ou utiliser:

sb.delete(0, sb.length());  // A bit slower as it is used to delete sub sequence.  

REMARQUE

Évitez de déclarer StringBufferou des StringBuilderobjets dans la boucle sinon cela créera de nouveaux objets à chaque itération. La création d'objets nécessite des ressources système, de l'espace et prend également du temps. Donc pour le long terme, évitez de les déclarer dans une boucle si possible.

Aditya Singh
la source
5
buf.delete(0,  buf.length());
Suraj Chandran
la source
5

Je suggère de créer un nouveau StringBuffer(ou même mieux StringBuilder) pour chaque itération. La différence de performances est vraiment négligeable, mais votre code sera plus court et plus simple.

Eli Acherkan
la source
2
Si vous avez des preuves d'une telle différence de performance, veuillez les partager. (Je serai également heureux de voir des statistiques sur les endroits où Java est le plus utilisé et sous quelle définition de "principalement".)
Eli Acherkan
1
Il est bien connu que l'allocation (nouveau mot-clé en Java) coûte plus cher que de simplement changer certains champs du même objet. Pour «la plupart du temps», avec lequel vous pourriez répliquer fortement, il y a plus de 1,5 milliard d'appareils Android, tous utilisant Java.
Louis CAD du
1
Je suis d'accord que dans certains cas, la lisibilité du code est plus importante que les performances, mais dans ce cas, ce n'est qu'une ligne de code! Et vous pouvez exécuter un benchmark qui vous prouvera que l'allocation et la création d'objets, ainsi que le ramasse-miettes sont plus chers que de ne pas le faire du tout. Comme nous sommes dans une boucle, c'est plus que pertinent.
Louis CAD du
1
J'ai également souscrit au point de vue de Knuth, mais son point de vue n'est pas toujours vrai. Ajouter une seule ligne pour potentiellement gagner des cycles CPU et de la mémoire n'est absolument pas un mal. Vous prenez l'idée trop juste. Notez qu'une boucle est généralement répétée plusieurs fois. Une boucle peut être répétée des milliers de fois, ce qui peut consommer de précieux mégaoctets sur un mobile (et également sur un serveur ou un ordinateur) si elle n'est pas optimisée avec soin.
Louis CAD
1
Je pense que nous avons tous les deux énoncé nos points de vue assez clairement, et je pense qu'une discussion plus approfondie ne sera pas bénéfique pour cette section de commentaires. Si vous pensez que ma réponse est incorrecte, trompeuse ou incomplète, alors vous - ou n'importe qui - êtes bienvenu pour la modifier et / ou la voter contre.
Eli Acherkan
5
public void clear(StringBuilder s) {
    s.setLength(0);
}

Usage:

StringBuilder v = new StringBuilder();
clear(v);

pour la lisibilité, je pense que c'est la meilleure solution.

Ido Kessler
la source
2

Déjà bonne réponse là-bas. Ajoutez simplement un résultat de référence pour la différence de performance StringBuffer et StringBuild, utilisez une nouvelle instance en boucle ou utilisez setLength (0) en boucle.

Le résumé est: Dans une grande boucle

  • StringBuilder est beaucoup plus rapide que StringBuffer
  • Créer une nouvelle instance StringBuilder en boucle n'a aucune différence avec setLength (0). (setLength (0) a un très très très petit avantage que de créer une nouvelle instance.)
  • StringBuffer est plus lent que StringBuilder en créant une nouvelle instance en boucle
  • setLength (0) de StringBuffer est extrêmement plus lent que la création d'une nouvelle instance en boucle.

Benchmark très simple (je viens de changer manuellement le code et de faire un test différent):

public class StringBuilderSpeed {
public static final char ch[] = new char[]{'a','b','c','d','e','f','g','h','i'};

public static void main(String a[]){
    int loopTime = 99999999;
    long startTime = System.currentTimeMillis();
    StringBuilder sb = new StringBuilder();
    for(int i = 0 ; i < loopTime; i++){
        for(char c : ch){
            sb.append(c);
        }
        sb.setLength(0);
    }
    long endTime = System.currentTimeMillis();
    System.out.println("Time cost: " + (endTime - startTime));
}

}

Nouvelle instance StringBuilder en boucle: coût en temps: 3693, 3862, 3624, 3742

StringBuilder setLength: Coût du temps: 3465, 3421, 3557, 3408

Nouvelle instance StringBuffer en boucle: coût en temps: 8327, 8324, 8284

StringBuffer setLength Coût du temps: 22878, 23017, 22894

Encore une fois StringBuilder setLength pour m'assurer que mon laboratoire n'a pas de problème à utiliser si longtemps pour StringBuffer setLength :-) Coût du temps: 3448

Hao
la source
0
StringBuffer sb = new SringBuffer();
// do something wiht it
sb = new StringBuffer();

je pense que ce code est plus rapide.

Binh Phung
la source
3
Cela aura des problèmes de performances. Il crée un nouvel objet après chaque itération
Aditya Singh
@AdityaSingh C'est une approche parfaitement raisonnable. N'assumez pas les problèmes de performances sans preuves.
shmosel
Assumer des choses basées sur une compréhension de ce qui se passe est la base de l'intelligence.
rghome