Strings Mutáveis: StringBuffer e StringBuilder em Java
O problema: a imutabilidade da String
Objetos da classe String
são imutáveis. Isso significa que qualquer operação que pareça modificar uma string — como concatenar ou substituir caracteres — cria um novo objeto String
na memória.
Em cenários com muitas manipulações, como a construção de uma string longa dentro de um loop, isso pode gerar consumo excessivo de memória e queda de desempenho.
A solução: StringBuffer
e StringBuilder
Para contornar esse problema, o Java oferece duas classes especializadas: StringBuffer
e StringBuilder
.
Elas representam strings mutáveis, permitindo alterações no conteúdo sem criar um novo objeto a cada modificação.
A diferença principal: StringBuffer
vs StringBuilder
As duas classes possuem a mesma API e construtores semelhantes. A diferença está na segurança em ambientes com múltiplas threads.
StringBuffer
— métodos sincronizados, o que garante segurança em aplicações multithread. Isso impede que múltiplas threads alterem o mesmo objeto simultaneamente de forma insegura, mas adiciona sobrecarga e reduz um pouco a performance.StringBuilder
— não é sincronizado, o que o torna mais rápido. É a escolha recomendada na maioria dos casos, especialmente em aplicações de thread única.
Regra prática: Prefira StringBuilder
pela performance, usando StringBuffer
apenas se precisar manipular a mesma string em múltiplas threads.
Capacidade e comprimento
StringBuffer
e StringBuilder
usam um buffer interno — um array de caracteres que pode crescer quando necessário. Isso evita criar novos objetos a cada modificação.
- Comprimento (
length()
) — número de caracteres que o conteúdo possui no momento. - Capacidade (
capacity()
) — tamanho máximo que o buffer pode armazenar antes de precisar crescer.
A capacidade inicial varia conforme a forma de criação:
Criando um StringBuilder
vazio
Capacidade inicial padrão: 16 caracteres.
StringBuilder sbVazio = new StringBuilder();
System.out.println(sbVazio.length()); // 0
System.out.println(sbVazio.capacity()); // 16
Criando com texto inicial
Capacidade inicial: comprimento do texto + 16.
StringBuilder sb = new StringBuilder("Java");
System.out.println(sb.length()); // 4
System.out.println(sb.capacity()); // 20 (4 + 16)
Crescimento de capacidade
O método ensureCapacity()
garante uma capacidade mínima.
Se o valor informado for maior que a capacidade atual, o buffer cresce de acordo com a fórmula:
Exemplo:
StringBuilder sb = new StringBuilder("Java"); // capacidade inicial: 20
sb.ensureCapacity(32); // 20 < 32, precisa crescer
System.out.println(sb.capacity()); // 42 ((20 * 2) + 2)
Convertendo para String
Para obter um objeto String
imutável, use toString()
:
StringBuilder sb = new StringBuilder("Hello");
String result = sb.toString();
Principais métodos
A maioria dos métodos modifica o próprio objeto em vez de criar um novo.
Adição e inserção
StringBuilder sb = new StringBuilder("Hello");
sb.append(" World"); // "Hello World"
StringBuilder sb2 = new StringBuilder("word");
sb2.insert(3, 'l'); // "world"
Remoção e substituição
StringBuilder sb = new StringBuilder("assembler");
sb.delete(0, 2); // "sembler"
sb.deleteCharAt(6); // "semble"
StringBuilder sb2 = new StringBuilder("hello world!");
sb2.replace(6, 11, "java"); // "hello java!"
Outros métodos úteis
StringBuilder sb = new StringBuilder("reverter");
sb.reverse(); // "retrever"
StringBuilder sb2 = new StringBuilder("Java");
sb2.setCharAt(0, 'L'); // "Lava"
// substring() não altera o conteúdo original
StringBuilder sb3 = new StringBuilder("hello java!");
String part = sb3.substring(6); // "java!"
Tabela comparativa
Característica | String | StringBuffer | StringBuilder |
---|---|---|---|
Mutabilidade | Imutável | Mutável | Mutável |
Performance | Lenta para alterações | Boa | Excelente |
Thread-Safe | Sim (imutável) | Sim (sincronizado) | Não |
Uso principal | Texto fixo | Construção de strings em multithread | Construção de strings em thread única (uso geral) |
Resumo – Strings Mutáveis em Java
String
é imutável; cada modificação cria um novo objeto.StringBuffer
eStringBuilder
são mutáveis, permitindo alterações sem criar novos objetos.StringBuffer
é sincronizado e seguro em múltiplas threads, mas mais lento.StringBuilder
é não sincronizado, mais rápido e indicado para uso geral.- Ambos usam um buffer interno com capacidade ajustável.
- Oferecem métodos como
append()
,insert()
,delete()
,replace()
,reverse()
,setCharAt()
esubstring()
.
📝 Exercícios
Tarefa 1 (Teoria)
Descrição: Um programador precisa concatenar 1.000 pequenas strings para formar uma única string longa dentro de um loop. Qual é a abordagem mais eficiente em termos de performance e por quê?
Alternativas:
- Usar a classe
String
e o operador+
, pois é o código mais simples de escrever. - Usar a classe
StringBuilder
e seu métodoappend()
, pois ela modifica a string internamente sem criar novos objetos a cada iteração. - Usar a classe
StringBuffer
, pois ela é sempre a opção mais rápida disponível.
Resposta
StringBuilder
e seu método append()
, pois ela modifica a string internamente sem criar novos objetos a cada iteração.
String
é imutável. Isso significa que a cada iteração do loop, a linha minhaString = minhaString + "nova_parte";
não modifica a string existente, mas cria um objeto String
completamente novo na memória, contendo o resultado da concatenação. Para 1.000 iterações, isso resulta na criação de 1.000 objetos intermediários e um grande impacto negativo na performance.
StringBuilder
foi projetado para ser mutável. Seu método append()
simplesmente adiciona os novos caracteres ao seu buffer interno, redimensionando-o apenas quando necessário. Isso evita a criação massiva de objetos, tornando-o a escolha ideal e muito mais eficiente para a construção de strings em loops.
Tarefa 2 (Prática)
Descrição: Complete o método montarUrl
para que ele use um StringBuilder
para construir e retornar uma URL a partir das partes fornecidas. A URL final deve ter o formato https://dominio.com/recurso?param=valor
.
Código inicial:
public class Main {
public static void main(String[] args) {
String dominio = "dominio.com";
String recurso = "recurso";
String parametro = "param";
String valor = "valor";
String url = montarUrl(dominio, recurso, parametro, valor);
System.out.println(url); // Resultado esperado: https://dominio.com/recurso?param=valor
}
static String montarUrl(String dominio, String recurso, String parametro, String valor) {
// Use um StringBuilder para construir a URL eficientemente.
// Retorne o resultado como uma String.
}
}
Resposta
public class Main {
public static void main(String[] args) {
String dominio = "dominio.com";
String recurso = "recurso";
String parametro = "param";
String valor = "valor";
String url = montarUrl(dominio, recurso, parametro, valor);
System.out.println(url);
}
static String montarUrl(String dominio, String recurso, String parametro, String valor) {
StringBuilder sb = new StringBuilder();
sb.append("https://");
sb.append(dominio);
sb.append("/");
sb.append(recurso);
sb.append("?");
sb.append(parametro);
sb.append("=");
sb.append(valor);
return sb.toString();
}
}
StringBuilder
e utiliza o método append()
repetidamente para adicionar cada parte da URL de forma eficiente, sem criar objetos String
intermediários a cada passo. No final, o método toString()
é chamado para converter o conteúdo mutável do StringBuilder
em um único objeto String
final, que é então retornado. Este é o caso de uso ideal para StringBuilder
.