Comparação Técnica entre StringBuffer e StringBuilder
As classes StringBuffer e StringBuilder compartilham uma hierarquia de herança comum, ambas estendendo a classe abstrata AbstractStringBuilder. Essa estrutura permite que compartilhem implementações básicas de operações de manipulação de strings mutáveis.
Mecanismo de Expansão de Capacidade
A capacidade de armazenamento é gerenciada por meio de um array de caracteres (char[]) não final, permitindo realocações dinâmicas. Quando o espaço disponível é insuficiente, ocorre a expansão automática através do método ensureCapacity:
public void ensureCapacity(int capacidadeMinima) { if (capacidadeMinima > 0) garantirCapacidadeInterna(capacidadeMinima); }
private void garantirCapacidadeInterna(int capacidadeMinima) { if (capacidadeMinima - value.length > 0) { value = Arrays.copyOf(value, calcularNovaCapacidade(capacidadeMinima)); } }
private int calcularNovaCapacidade(int capacidadeMinima) { int novaCapacidade = (value.length << 1) + 2; if (novaCapacidade - capacidadeMinima < 0) { novaCapacidade = capacidadeMinima; } return (novaCapacidade <= 0 || MAX_ARRAY_SIZE - novaCapacidade < 0) ? resolverCapacidadeExcessiva(capacidadeMinima) : novaCapacidade; }
private int resolverCapacidadeExcessiva(int capacidadeMinima) { if (Integer.MAX_VALUE - capacidadeMinima < 0) { throw new OutOfMemoryError(); } return (capacidadeMinima > MAX_ARRAY_SIZE) ? capacidadeMinima : MAX_ARRAY_SIZE; }
</div>O cálculo padrão dobra a capacidade atual e aidciona 2 posições. Em casos de overflow ou necessidade de grandes alocações, são aplicadas estratégias de contingência.
#### Segurança em Ambiente Concorrente
Para demonstrar as diferenças de thread safety, analisamos os métodos críticos:
- `StringBuffer`: Métodos sincronizados com modificador `synchronized`<div>```
@Override
public synchronized StringBuffer append(long valor) {
toStringCache = null;
super.append(valor);
return this;
}
```
</div>
- `StringBuilder`: Implementação não sincronizada <div>```
@Override
public StringBuilder append(double valor) {
super.append(valor);
return this;
}
```
</div>
#### Teste de Concorrência
Um teste com 1000 threads simultâneas demonstra as diferenças práticas:
<div>```
import java.util.concurrent.CountDownLatch;
public class TesteConcorrencia {
public static void main(String[] args) throws InterruptedException {
StringBuffer bufferThreadSafe = new StringBuffer();
StringBuilder builderNaoThreadSafe = new StringBuilder();
CountDownLatch latch1 = new CountDownLatch(1000);
CountDownLatch latch2 = new CountDownLatch(1000);
// Teste com StringBuilder
for (int i = 0; i < 1000; i++) {
new Thread(() -> {
try {
builderNaoThreadSafe.append('a');
} finally {
latch1.countDown();
}
}).start();
}
// Teste com StringBuffer
for (int i = 0; i < 1000; i++) {
new Thread(() -> {
try {
bufferThreadSafe.append('a');
} finally {
latch2.countDown();
}
}).start();
}
latch1.await();
System.out.println("Tamanho StringBuilder: " + builderNaoThreadSafe.length());
latch2.await();
System.out.println("Tamanho StringBuffer: " + bufferThreadSafe.length());
}
}
- StringBuffer sempre retorna 1000 caracteres (comportamento consistente)
- StringBuilder frequentemente retorna valores menores que 1000 (colisões de acesso concorrente)
Conclusão Técnica
- Ambas compartilham implementação abstrata para operações básicas
- O mecanismo de expansão é implementado na classe pai
AbstractStringBuilder StringBufferprioriza segurança com sincronização, enquantoStringBuilderotimiza desempenho em contextos single-thread- A escolha entre elas deve considerar requisitos de concorrência e performence