String, StringBuffer e StringBuilder

213

Por favor, diga-me uma situação em tempo real para comparar String, StringBuffere StringBuilder?

JavaUser
fonte

Respostas:

378

Diferença de mutabilidade:

Stringé imutável , se você tentar alterar seus valores, um outro objeto é criado, enquanto StringBuffere StringBuildersão mutáveis assim que podem mudar seus valores.

Diferença de segurança da linha:

A diferença entre StringBuffere StringBuilderé que StringBufferé seguro para threads. Portanto, quando o aplicativo precisar ser executado apenas em um único encadeamento, é melhor usá-lo StringBuilder. StringBuilderé mais eficiente que StringBuffer.

Situações:

  • Se sua string não for alterada, use uma classe String porque um Stringobjeto é imutável.
  • Se sua string puder mudar (exemplo: muita lógica e operações na construção da string) e somente será acessada a partir de um único encadeamento, o uso de a StringBuilderé bom o suficiente.
  • Se sua string puder mudar e será acessada a partir de vários threads, use a StringBufferporque StringBufferé síncrona para que você tenha segurança de thread.
bakkal
fonte
16
Além disso, o uso de String para operações lógicas é bastante lento e não é recomendado, pois a JVM converte a String em um StringBuffer no bytecode. Muita sobrecarga é desperdiçada na conversão de String para StringBuffer e, em seguida, de volta para String novamente.
Pieter van Niekerk
2
Então, Stringsquando alteramos o valor, outro objeto é criado. A referência de objeto antigo é anulada para que possa ser coletada pelo lixo GCou até mesmo coletada?
Harsh Vardhan
@PietervanNiekerk O que você quer dizer com operações lógicas?
emlai
operações lógicas, quero dizer, são básicas de String. Agora, uma coisa que eu gostaria de perguntar, como declarado por @Peter, devemos começar a usar StringBuffer em vez de String em todos os casos ou há alguns casos específicos?
JavaDragon
@bakkal Posso usar todos os métodos Stringcom StringBuilder?
roottraveller
47
  • Você usa Stringquando uma estrutura imutável é apropriada; obter uma nova sequência de caracteres de a Stringpode levar a uma penalidade de desempenho inaceitável, seja no tempo ou na memória da CPU (a obtenção de substrings é eficiente na CPU porque os dados não são copiados, mas isso significa que uma quantidade potencialmente muito maior de dados pode permanecer alocada).
  • Você usa StringBuilderquando precisa criar uma sequência de caracteres mutável, geralmente para concatenar várias seqüências de caracteres juntas.
  • Você usa StringBuffernas mesmas circunstâncias que usaria StringBuilder, mas quando alterações na cadeia subjacente devem ser sincronizadas (porque vários segmentos estão lendo / modificando no buffer da cadeia).

Veja um exemplo aqui .

Artefacto
fonte
2
conciso, mas incompleto, perde o motivo fundamental para usar o StringBuilder / Buffer e isso é reduzir ou eliminar a re-alocação e cópia de matriz do comportamento regular da concatenação de String.
1
"Você usa String quando está lidando com seqüências imutáveis" - não faz sentido. Instâncias de String SÃO imutáveis, portanto, talvez o comentário deva ser "Use String quando o uso de memória devido à imutabilidade não for importante". A resposta aceita cobre suas bases muito bem.
precisa saber é o seguinte
28

O básico:

Stringé uma classe imutável, não pode ser alterada. StringBuilderé uma classe mutável que pode ser anexada, caracteres substituídos ou removidos e, finalmente, convertidos em a String StringBufferé a versão sincronizada original deStringBuilder

Você deve preferir StringBuilderem todos os casos em que apenas um único thread acesse seu objeto.

Os detalhes:

Observe também que StringBuilder/Buffersnão é mágico, eles apenas usam uma matriz como um objeto de apoio e que a matriz precisa ser realocada sempre que ficar cheia. Certifique-se de criar seus StringBuilder/Bufferobjetos suficientemente grandes originalmente, onde eles não precisem ser constantemente redimensionados toda vez que forem .append()chamados.

O redimensionamento pode ficar muito degenerado. Basicamente, redimensiona a Matriz de apoio para 2 vezes seu tamanho atual sempre que precisar ser expandida. Isso pode resultar em grandes quantidades de RAM sendo alocadas e não usadas quando as StringBuilder/Bufferclasses começam a crescer.

Em Java String x = "A" + "B";usa um StringBuildernos bastidores. Portanto, para casos simples, não há benefício em declarar o seu. Mas se você estiver construindo Stringobjetos grandes, digamos menos que 4k, declarar StringBuilder sb = StringBuilder(4096);é muito mais eficiente que concatenação ou usar o construtor padrão, que tem apenas 16 caracteres. Se o seu Stringfor menor que 10k, inicialize-o com o construtor para 10k para ser seguro. Mas, se for inicializado para 10k, você escrever 1 caractere a mais que 10k, será realocado e copiado para uma matriz de 20k. Portanto, inicializar alto é melhor do que baixo.

No caso de redimensionamento automático, no 17º caractere, a Matriz de backup é re-alocada e copiada para 32 caracteres; no 33º caractere, isso acontece novamente e você é re-alocado e copia a Matriz em 64 caracteres. Você pode ver como isso degenera em muitas re-alocações e cópias, o que você realmente está tentando evitar StringBuilder/Bufferem primeiro lugar.

Isto é do código-fonte do JDK 6 para 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);
    }

Uma prática recomendada é inicializar StringBuilder/Bufferum pouco maior do que você pensa que precisará, se não souber de imediato qual Stringserá o tamanho , mas você pode adivinhar. Uma alocação de um pouco mais de memória do que você precisa será melhor do que muitas re-alocações e cópias.

Lembre-se também de inicializar a StringBuilder/Buffercom a, Stringpois isso alocará apenas o tamanho dos caracteres String + 16, que na maioria dos casos apenas iniciarão o ciclo degenerado de re-alocação e cópia que você está tentando evitar. O seguinte é direto do código-fonte do Java 6.

public StringBuilder(String str) {
    super(str.length() + 16);
    append(str);
    }

Se por acaso você acabar com uma instância StringBuilder/Bufferque você não criou e não pode controlar o construtor chamado, existe uma maneira de evitar o comportamento degenerado de realocar e copiar. Ligue .ensureCapacity()com o tamanho em que você deseja garantir que o resultado Stringserá adequado.

As alternativas:

Assim como uma nota, se você está fazendo realmente pesado String construção e manipulação, há uma alternativa muito mais orientado para o desempenho chamada Cordas .

Outra alternativa é criar uma StringListimplementação subclassificando ArrayList<String>e adicionando contadores para rastrear o número de caracteres em todas as .append()operações de mutação da lista, substituindo-o .toString()para criar StringBuildero tamanho exato de que você precisa, percorrendo a lista e construindo a saída, você pode até tornar StringBuilderuma variável de instância e 'armazenar em cache' os resultados .toString()e só precisa gerá-la novamente quando algo mudar.

Também não se esqueça de String.format()criar saída formatada fixa, que pode ser otimizada pelo compilador à medida que a melhora.

user177800
fonte
1
Será que String x = "A" + "B";realmente compilar a ser um StringBuilder? Por que não basta compilar String x = "AB";, ele deve usar um StringBuilder apenas se os componentes não forem conhecidos no momento da compilação.
Matt Greer
ele pode otimizar as constantes String, não me lembro da última vez que descompilei o código de bytes, mas sei que, se houver alguma variável, ele usará uma implementação StringBuilder com certeza. Você pode fazer o download do código fonte do JDK e descobrir por si mesmo. "A" + "B" é um exemplo artificial, com certeza.
Eu estava pensando sobre String.format (). Eu realmente nunca vi isso sendo usado em projetos. Geralmente é o StringBuilder. Bem, geralmente é "A" + "B" + "C" porque as pessoas são preguiçosas;) eu costumava sempre usar um StringBuilder, mesmo que apenas duas cordas fossem concatenadas, porque no futuro, talvez mais cordas fossem anexadas . Eu nunca usei String.format () principalmente porque nunca me lembrei do JDK que foi introduzido - vejo que é o JDK1.5, usaria em favor de outras opções.
jamiebarrow
9

Você quer dizer concatenação?

Exemplo do mundo real: você deseja criar uma nova string dentre muitas outras .

Por exemplo, para enviar uma mensagem:

Corda

String s = "Dear " + user.name + "<br>" + 
" I saw your profile and got interested in you.<br>" +
" I'm  " + user.age + "yrs. old too"

StringBuilder

String s = new StringBuilder().append.("Dear ").append( user.name ).append( "<br>" ) 
          .append(" I saw your profile and got interested in you.<br>") 
          .append(" I'm  " ).append( user.age ).append( "yrs. old too")
          .toString()

Ou

String s = new StringBuilder(100).appe..... etc. ...
// The difference is a size of 100 will be allocated upfront as  fuzzy lollipop points out.

StringBuffer (a sintaxe é exatamente como no StringBuilder, os efeitos diferem)

Sobre

StringBuffer vs. StringBuilder

O primeiro é sincronizado e mais tarde não.

Portanto, se você invocá-lo várias vezes em um único encadeamento (que é 90% dos casos), StringBuilderserá executado muito mais rápido porque não parará para ver se possui o bloqueio do encadeamento.

Portanto, é recomendável usar StringBuilder(a menos que você tenha mais de um encadeamento acessando ao mesmo tempo, o que é raro)

Stringa concatenação ( usando o operador + ) pode ser otimizada pelo compilador para usar StringBuilderembaixo, portanto, não é mais necessário se preocupar, nos dias anteriores do Java, isso era algo que todo mundo diz que deve ser evitado a todo custo, porque toda concatenação criou um novo objeto String. Compiladores modernos não fazem mais isso, mas ainda é uma boa prática usarStringBuilder no caso de você usar um compilador "antigo".

editar

Apenas para quem está curioso, é isso que o compilador faz para esta classe:

class StringConcatenation {
    int x;
    String literal = "Value is" + x;
    String builder = new StringBuilder().append("Value is").append(x).toString();
}

javap -c StringConcatenation

Compiled from "StringConcatenation.java"
class StringConcatenation extends java.lang.Object{
int x;

java.lang.String literal;

java.lang.String builder;

StringConcatenation();
  Code:
   0:   aload_0
   1:   invokespecial   #1; //Method java/lang/Object."<init>":()V
   4:   aload_0
   5:   new #2; //class java/lang/StringBuilder
   8:   dup
   9:   invokespecial   #3; //Method java/lang/StringBuilder."<init>":()V
   12:  ldc #4; //String Value is
   14:  invokevirtual   #5; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
   17:  aload_0
   18:  getfield    #6; //Field x:I
   21:  invokevirtual   #7; //Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
   24:  invokevirtual   #8; //Method java/lang/StringBuilder.toString:()Ljava/lang/String;
   27:  putfield    #9; //Field literal:Ljava/lang/String;
   30:  aload_0
   31:  new #2; //class java/lang/StringBuilder
   34:  dup
   35:  invokespecial   #3; //Method java/lang/StringBuilder."<init>":()V
   38:  ldc #4; //String Value is
   40:  invokevirtual   #5; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
   43:  aload_0
   44:  getfield    #6; //Field x:I
   47:  invokevirtual   #7; //Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
   50:  invokevirtual   #8; //Method java/lang/StringBuilder.toString:()Ljava/lang/String;
   53:  putfield    #10; //Field builder:Ljava/lang/String;
   56:  return

}

As linhas numeradas de 5 a 27 são para a String denominada "literal"

As linhas numeradas de 31 a 53 são para a String denominada "construtor"

Não há diferença, exatamente o mesmo código é executado para as duas strings.

OscarRyz
fonte
2
este é um exemplo muito ruim. Iniciar o StringBuilder com "Dear" significa que o primeiro .append () causará uma realocação e cópia. Negando completamente qualquer eficiência que você esteja tentando obter em relação à concatenação "normal". Um exemplo melhor seria criá-lo com um tamanho inicial que conterá todo o conteúdo da String final.
1
Geralmente, não é uma boa prática usar uma StringBuilderconcatenação de String para executar no lado direito da tarefa. Qualquer boa implementação usará um StringBuilder nos bastidores, como você diz. Além disso, seu exemplo "a" + "b"seria compilado em um único literal, "ab"mas se você o usasse StringBuilder, resultaria em duas chamadas desnecessárias para append().
Mark Peters
@ Mark, eu não pretendia usar, "a"+"b"mas dizer o que era uma concatenação de String, alterei para ser explícito. O que você não diz é: por que não é uma boa prática fazê-lo? É exatamente isso que um compilador (moderno) faz. @ distorcido, eu concordo, especialmente quando você sabe qual seria o tamanho da string final (aprox).
OscarRyz
1
Não acho que seja uma prática particularmente ruim , mas certamente não gostaria de receber nenhuma recomendação para fazer isso em geral. É desajeitado, difícil de ler e excessivamente detalhado. Além disso, incentiva erros como o seu, onde você está dividindo dois literais que, de outra forma, poderiam ser compilados como um. Somente se os perfis me dissessem que isso fazia diferença eu faria do seu jeito.
Mark Peters
@ Mark Entendi. Eu estava pensando mais no "grande pedaço de código como um modelo" e não realmente em todas as cadeias de caracteres regulares literais. Mas sim, eu concordo, uma vez que eles fazem a mesma coisa hoje em dia, não faz sentido (10 anos atrás era uma razão para rejeitar uma mudança em uma revisão do código) :)
OscarRyz
8
-------------------------------------------------- --------------------------------
                  String StringBuffer StringBuilder
-------------------------------------------------- --------------------------------                 
Área de armazenamento | Pilha de Heap de Conjunto de Cordas Constantes
Modificável Não (imutável) Sim (mutável) Sim (mutável)
Segmento de Segmento | Sim Sim Não
 Desempenho | Rápido Muito lento Rápido
-------------------------------------------------- --------------------------------
Abhijit Maity
fonte
Por que o desempenho da String é rápido e o desempenho do StringBuffer muito lento?
gaurav
1
@gaurav você pode ler seu código fonte, todos os métodos no StringBuffer são synchronisede é por isso .
Hearen
8

Família String

Corda

O String classrepresenta cadeias de caracteres. Todos os literais de cadeia de caracteres no programa Java, como "abc"são implementados como instâncias desta classe.

Objetos de string são imutáveis, uma vez criados, não podemos mudar. ( Strings são constantes )

  • Se uma String for criada usando o construtor ou o método, essas strings serão armazenadas na Memória de Pilha e também SringConstantPool. Porém, antes de salvar no pool, ele invoca o intern()método para verificar a disponibilidade do objeto com o mesmo conteúdo no pool usando o método equals. Se a cópia de cadeia estiver disponível no pool, retornará a referência. Caso contrário, o objeto String será adicionado ao pool e retornará a referência.

    • A linguagem Java fornece suporte especial ao operador de concatenação de strings ( +) e à conversão de outros objetos em strings. A concatenação de strings é implementada por meio da classe StringBuilder (ou StringBuffer) e seu método de acréscimo.

    String heapSCP = new String("Yash");
    heapSCP.concat(".");
    heapSCP = heapSCP + "M";
    heapSCP = heapSCP + 777;
    
    // For Example: String Source Code 
    public String concat(String str) {
        int otherLen = str.length();
        if (otherLen == 0) {
            return this;
        }
        int len = value.length;
        char buf[] = Arrays.copyOf(value, len + otherLen);
        str.getChars(buf, len);
        return new String(buf, true);
    }
  • Os literais de string são armazenados em StringConstantPool.

    String onlyPool = "Yash";

StringBuilder e StringBuffer são uma sequência mutável de caracteres. Isso significa que é possível alterar o valor desses objetos. StringBuffer possui os mesmos métodos que o StringBuilder, mas cada método no StringBuffer é sincronizado, portanto, é seguro para threads.

  • Os dados StringBuffer e StringBuilder podem ser criados apenas usando o novo operador. Portanto, eles são armazenados na memória Heap.

  • Instâncias do StringBuilder não são seguras para uso por vários threads. Se essa sincronização for necessária, é recomendável que StringBuffer seja usado.

    StringBuffer threadSafe = new StringBuffer("Yash");
    threadSafe.append(".M");
    threadSafe.toString();
    
    StringBuilder nonSync = new StringBuilder("Yash");
    nonSync.append(".M");
    nonSync.toString();
  • StringBuffer e StringBuilder está tendo um métodos especiais como., replace(int start, int end, String str)E reverse().

    NOTA : StringBuffer e SringBuilder são mutáveis, pois fornecem a implementação de Appendable Interface.


Quando usar qual.

  • Se você não vai mudar o valor toda vez, é melhor usar String Class. Como parte de Genéricos, se você deseja Classificar Comparable<T>ou comparar um valor, vá em String Class.

    //ClassCastException: java.lang.StringBuffer cannot be cast to java.lang.Comparable
    Set<StringBuffer> set = new TreeSet<StringBuffer>();
    set.add( threadSafe );
    System.out.println("Set : "+ set);
  • Se você for modificar o valor toda vez que for para StringBuilder, que é mais rápido que StringBuffer. Se vários threads estiverem modificando o valor, vá para StringBuffer.

Yash
fonte
4

Além disso, StringBufferé seguro para threads, o que StringBuildernão é.

Portanto, em uma situação em tempo real, quando diferentes threads estão acessando, StringBuilderpode ter um resultado indeterminado.

Lars Andren
fonte
3

Observe que se você estiver usando o Java 5 ou mais recente, deverá usar em StringBuildervez de StringBuffer. Na documentação da API:

A partir do release JDK 5, essa classe foi complementada por uma classe equivalente projetada para uso por um único encadeamento StringBuilder,. A StringBuilderclasse geralmente deve ser usada preferencialmente a esta, pois suporta todas as mesmas operações, mas é mais rápida, pois não realiza sincronização.

Na prática, você quase nunca usará isso de vários threads ao mesmo tempo, portanto, a sincronização que StringBufferfaz é quase sempre sobrecarga desnecessária.

Jesper
fonte
3

Pessoalmente, não acho que exista nenhum uso no mundo real StringBuffer. Quando é que eu gostaria de me comunicar entre vários threads manipulando uma sequência de caracteres? Isso não parece útil, mas talvez eu ainda não tenha visto a luz :)

fredoverflow
fonte
3

A diferença entre String e as outras duas classes é que String é imutável e as outras duas são classes mutáveis.

Mas por que temos duas classes para o mesmo propósito?

A razão é que o StringBufferThread é seguro e StringBuildernão é. StringBuilderé uma nova classe ativada StringBuffer Apie foi introduzida JDK5e sempre é recomendada se você estiver trabalhando em um ambiente de encadeamento único, pois é muitoFaster

Para obter detalhes completos, você pode ler http://www.codingeek.com/java/stringbuilder-and-stringbuffer-a-way-to-create-mutable-strings-in-java/

Hitesh Garg
fonte
2

Em java, String é imutável. Sendo imutável, queremos dizer que, uma vez que uma String é criada, não podemos alterar seu valor. StringBuffer é mutável. Depois que um objeto StringBuffer é criado, apenas anexamos o conteúdo ao valor do objeto em vez de criar um novo objeto. StringBuilder é semelhante ao StringBuffer, mas não é seguro para threads. Os métodos do StingBuilder não são sincronizados, mas em comparação com outras Strings, o Stringbuilder roda mais rápido. Você pode aprender a diferença entre String, StringBuilder e StringBuffer implementando-os.

rajat ghai
fonte