Cancellazione di un buffer / generatore di stringhe dopo il ciclo


122

Come si cancella il buffer di stringa in Java dopo un ciclo in modo che l'iterazione successiva utilizzi un buffer di stringa chiaro?


2
Un'altra domanda: perché usi un SB solo per un'iterazione del ciclo? C'è un'altra iterazione interna? SB non è degno se vuoi semplicemente fare A + B + C + D (il compilatore Java userà internamente un SB). È utile se vuoi aggiungere in modo condizionale parti di stringhe, ma altrimenti ... usa semplicemente "+".
helios

Risposte:


135

Un'opzione è utilizzare il metodo di eliminazione come segue:

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

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

Un'altra opzione (bit più pulita) usa setLength (int len) :

sb.setLength(0);

Vedi Javadoc per maggiori informazioni:


15
Qualcosa di un po 'meno spazzatura sarebbe semplicemente dichiarare lo StringBuffer all'interno del ciclo.
Mark Elliot,

11
Ah, penso che sb.setLength (0); è più pulito ed efficiente che dichiararlo all'interno del ciclo. La tua soluzione va contro il vantaggio in termini di prestazioni dell'utilizzo di StringBuffer ...
Jon

6
Penso che il vantaggio in termini di prestazioni derivi dalla mutevolezza delle stringhe, non dal salvataggio dell'istanza. ecco un rapido test delle iterazioni 1e8: inside loop (2.97s): ideone.com/uyyTL14w , outside loop (2.87s): ideone.com/F9lgsIxh
Mark Elliot,

6
A proposito di prestazioni: a meno che non si acceda al codice in uno scenario multi-thread, dovresti usare StringBuilder invece di StringBuffer - vedi javadoc: "Dove possibile, si consiglia di utilizzare questa classe preferibilmente a StringBuffer in quanto sarà più veloce in la maggior parte delle implementazioni ".
Rahel Lüthy

4
L'unico vantaggio di creare l'OdV all'esterno è non perdere il carattere interno (potenzialmente lungo) [] di esso. Se nel primo iteratore è cresciuto abbastanza, il secondo ciclo non avrà bisogno di ridimensionare alcun carattere []. Ma per ottenere il vantaggio il "metodo clear" dovrà preservare la dimensione dell'array interno. setLength lo fa ma imposta anche su \ u0000 tutti i caratteri non utilizzati in SB, quindi è meno performante che creare semplicemente un nuovo SB con una buona capacità iniziale. Dichiarare all'interno del ciclo è meglio.
helios

48

Il modo più semplice per riutilizzare StringBufferè utilizzare il metodosetLength()

public void setLength(int newLength)

Potresti avere il caso come

StringBuffer sb = new StringBuffer("HelloWorld");
// after many iterations and manipulations
sb.setLength(0);
// reuse sb

2
@ MMahmoud, Nell'esempio di codice dovrebbe leggere setLengthinvece di setlength.
OnaBai

Quando vedo il codice sorgente setLength, come funziona ( gist.github.com/ebuildy/e91ac6af2ff8a6b1821d18abf2f8c9e1 ) qui il conteggio sarà sempre maggiore di newLength (0)?
Thomas Decaux

20

Hai due opzioni:

O usa:

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

Oppure usa:

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

NOTA

Evita di dichiarare StringBuffero StringBuilderoggetti all'interno del ciclo altrimenti creerà nuovi oggetti ad ogni iterazione. La creazione di oggetti richiede risorse di sistema, spazio e richiede anche tempo. Quindi, a lungo termine, evita di dichiararli all'interno di un ciclo, se possibile.



5

Suggerisco di crearne uno nuovo StringBuffer(o anche migliore StringBuilder) per ogni iterazione. La differenza di prestazioni è davvero trascurabile, ma il codice sarà più breve e più semplice.


2
Se hai qualche prova di tale differenza di prestazioni, condividila. (Sarò anche felice di vedere le statistiche su dove Java è maggiormente utilizzato e sotto quale definizione di "principalmente".)
Eli Acherkan

1
È noto che l'allocazione (nuova parola chiave in Java) è più costosa rispetto alla semplice modifica di alcuni campi dello stesso oggetto. Per "principalmente", che potresti ripagare con molto, ci sono più di 1,5 miliardi di dispositivi Android, tutti che utilizzano Java.
Louis CAD

1
Sono d'accordo che in alcuni casi la leggibilità del codice è più importante delle prestazioni, ma in questo caso è solo una riga di codice! E puoi eseguire un benchmark che ti dimostrerà che l'allocazione e la creazione di oggetti, inoltre, la raccolta dei rifiuti è più costoso che non farlo affatto. Dato che siamo in un ciclo, è più che rilevante.
Louis CAD

1
Ho anche sottoscritto il punto di vista di Knuth, ma il suo punto di vista non è sempre vero. Aggiungere solo una riga per ottenere potenzialmente cicli di CPU e memoria non è assolutamente dannoso. Stai prendendo l'idea troppo chiara. Notare che un ciclo viene solitamente ripetuto molte volte. Un ciclo può essere ripetuto migliaia di volte, che può consumare preziosi megabyte su un cellulare (e anche su un server o un computer) se non ottimizzato con cura.
Louis CAD

1
Penso che entrambi abbiamo affermato abbastanza chiaramente i nostri punti di vista e ritengo che ulteriori discussioni non saranno utili per questa sezione di commenti. Se ritieni che la mia risposta sia errata, fuorviante o incompleta, allora tu - o chiunque altro - siete assolutamente invitati a modificarla e / o a votarla negativamente.
Eli Acherkan

5
public void clear(StringBuilder s) {
    s.setLength(0);
}

Uso:

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

per la leggibilità, penso che questa sia la soluzione migliore.


2

Già buona risposta lì. Basta aggiungere un risultato di benchmark per StringBuffer e la differenza di prestazioni di StringBuild usa una nuova istanza in loop o usa setLength (0) in loop.

Il riepilogo è: In un ampio ciclo

  • StringBuilder è molto più veloce di StringBuffer
  • La creazione di una nuova istanza di StringBuilder in loop non ha differenze con setLength (0). (setLength (0) ha un vantaggio molto molto piccolo rispetto alla creazione di una nuova istanza.)
  • StringBuffer è più lento di StringBuilder in quanto crea una nuova istanza in loop
  • setLength (0) di StringBuffer è estremamente più lento rispetto alla creazione di una nuova istanza in loop.

Benchmark molto semplice (ho appena cambiato manualmente il codice e ho fatto un test diverso):

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));
}

}

Nuova istanza StringBuilder in loop: costo del tempo: 3693, 3862, 3624, 3742

StringBuilder setLength: Time cost: 3465, 3421, 3557, 3408

Nuova istanza StringBuffer in loop: costo del tempo: 8327, 8324, 8284

StringBuffer setLength Costo del tempo: 22878, 23017, 22894

Ancora una volta StringBuilder setLength per garantire che non il mio labtop abbia qualche problema da usare così a lungo per StringBuffer setLength :-) Costo del tempo: 3448


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

penso che questo codice sia più veloce.


3
Ciò avrà problemi di prestazioni. Crea un nuovo oggetto dopo ogni iterazione
Aditya Singh

@AdityaSingh È un approccio perfettamente ragionevole. Non dare per scontato problemi di prestazioni senza prove.
shmosel

Assumere le cose basate sulla comprensione di ciò che sta accadendo è la base dell'intelligenza.
rghome
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.