Limpar um buffer / construtor de sequência após o loop

122

Como você limpa o buffer de cadeias de caracteres em Java após um loop para que a próxima iteração use um buffer de cadeias de caracteres limpas?

cachoeira
fonte
2
Outra pergunta: por que você usa um SB apenas para uma iteração do loop? Existe outra iteração interna? O SB não vale a pena se você simplesmente deseja fazer A + B + C + D (o compilador Java usará internamente um SB). Ajuda se você deseja adicionar condicionalmente partes de strings, mas caso contrário ... basta usar "+".
helios

Respostas:

135

Uma opção é usar o método delete da seguinte maneira:

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

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

Outra opção (limpador de bits) usa setLength (int len) :

sb.setLength(0);

Veja Javadoc para mais informações:

Jon
fonte
15
Algo um pouco menos inútil seria apenas declarar o StringBuffer dentro do loop.
Mark Elliot
11
Ah, acho que sb.setLength (0); é mais limpo e mais eficiente do que declará-lo dentro do loop. Sua solução vai contra o benefício de usar StringBuffer desempenho ...
Jon
6
Eu acho que o benefício de desempenho vem da mutabilidade de string, não de salvar a instanciação. aqui está um teste rápido de 1E8 iterações: loop dentro (2.97s): ideone.com/uyyTL14w , laço exterior (2.87s): ideone.com/F9lgsIxh
Mark Elliot
6
Falando em desempenho: a menos que seu código seja acessado em um cenário multithread, você deve usar StringBuilder em vez de StringBuffer - consulte javadoc: "Onde possível, é recomendável que essa classe seja usada preferencialmente a StringBuffer, pois será mais rápido em maioria das implementações ".
Rahel Lüthy
4
O único benefício de criar o SB externo não é perder o caráter interno (potencialmente longo) [] dele. Se no primeiro iterador crescer o suficiente, o segundo loop não precisará redimensionar nenhum caractere []. Mas, para obter a vantagem, o "método claro" precisará preservar o tamanho da matriz interna. setLength faz isso, mas também define para \ u0000 todos os caracteres não usados ​​no SB, por isso é menos eficiente do que simplesmente criar um novo SB com uma boa capacidade inicial. Declarar dentro do loop é melhor.
helios
48

A maneira mais fácil de reutilizar o StringBufferé usar o métodosetLength()

public void setLength(int newLength)

Você pode ter o caso como

StringBuffer sb = new StringBuffer("HelloWorld");
// after many iterations and manipulations
sb.setLength(0);
// reuse sb
Mossaddeque Mahmood
fonte
2
@ MMahmoud, No exemplo de código, ele deve ler em setLengthvez de setlength.
OnaBai
Quando vejo a fonte do código setLength, como está funcionando ( gist.github.com/ebuildy/e91ac6af2ff8a6b1821d18abf2f8c9e1 ) aqui a contagem será sempre maior que newLength (0)?
Thomas Decaux
20

Você tem duas opções:

Ou use:

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

Ou use:

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

NOTA

Evite declarar StringBufferou StringBuilderobjetos dentro do loop, caso contrário, ele criará novos objetos a cada iteração. A criação de objetos requer recursos do sistema, espaço e também leva tempo. Portanto, a longo prazo, evite declará-los em um loop, se possível.

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

Sugiro criar um novo StringBuffer(ou até melhor StringBuilder) para cada iteração. A diferença de desempenho é realmente insignificante, mas seu código será mais curto e mais simples.

Eli Acherkan
fonte
2
Se você tiver alguma evidência dessa diferença de desempenho, compartilhe-a. (Eu estarei também contente de ver estatísticas de onde Java é utilizado, principalmente, e em que a definição de "principalmente".)
Eli Acherkan
1
É sabido que a alocação (nova palavra-chave em Java) é mais cara do que apenas alterar alguns campos do mesmo objeto. Para "principalmente", com o qual você poderia substituir muito, existem mais de 1,5 bilhão de dispositivos Android, todos usando Java.
Louis CAD
1
Concordo que, em alguns casos, a legibilidade do código é mais importante que o desempenho, mas, neste caso, é apenas uma linha de código! E você pode executar uma referência que provará que a alocação e a criação de objetos, além da coleta de lixo, são mais caras do que não serem realizadas. Como estamos em um loop, é mais do que relevante.
Louis CAD,
1
Também subscrevi a visão de Knuth, mas seu ponto de vista nem sempre é verdadeiro. Adicionar apenas uma linha para potencialmente ganhar ciclos de CPU e memória não é absolutamente ruim. Você está levando a idéia muito a sério. Observe que um loop geralmente é repetido várias vezes. Um loop pode ser repetido milhares de vezes, que pode consumir megabytes preciosos em um celular (e também em um servidor ou computador) se não for otimizado com cuidado.
Louis CAD,
1
Penso que ambos declarámos os nossos pontos de vista com clareza suficiente e considero que uma discussão mais aprofundada não será benéfica para esta secção de comentários. Se você acha que minha resposta é incorreta, enganosa ou incompleta, você - ou qualquer pessoa - é bem-vinda para editá-la e / ou votá-la.
Eli Acherkan # 26/15
5
public void clear(StringBuilder s) {
    s.setLength(0);
}

Uso:

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

para facilitar a leitura, acho que essa é a melhor solução.

Ido Kessler
fonte
2

Já boa resposta lá. Basta adicionar um resultado de benchmark para que a diferença de desempenho StringBuffer e StringBuild use nova instância no loop ou use setLength (0) no loop.

O resumo é: Em um loop grande

  • StringBuilder é muito mais rápido que StringBuffer
  • Criar nova instância StringBuilder em loop não tem diferença com setLength (0). (setLength (0) tem uma vantagem muito, muito pequena, do que criar uma nova instância.)
  • StringBuffer é mais lento que StringBuilder ao criar nova instância no loop
  • setLength (0) de StringBuffer é extremamente mais lento que criar uma nova instância em loop.

Referência muito simples (eu mudei manualmente o código e fiz testes diferentes):

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

}

Nova instância StringBuilder em loop: Custo do tempo: 3693, 3862, 3624, 3742

StringBuilder setLength: Custo do tempo: 3465, 3421, 3557, 3408

Nova instância StringBuffer no loop: Custo do tempo: 8327, 8324, 8284

StringBuffer setLength Custo de tempo: 22878, 23017, 22894

Novamente StringBuilder setLength para garantir que meu labtop não tenha problemas para usar tanto tempo como StringBuffer setLength :-) Custo de tempo: 3448

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

Eu acho que esse código é mais rápido.

Binh Phung
fonte
3
Isso terá problemas de desempenho. Ele cria um novo objeto após cada iteração
Aditya Singh
@AdityaSingh É uma abordagem perfeitamente razoável. Não assuma problemas de desempenho sem evidências.
shmosel
Assumir que as coisas com base no entendimento do que está acontecendo é a base da inteligência.
Rgome