Veja neste artigo uma classe simples da linguagem Java, mas que é usada indevidamente por muitos desenvolvedores, a classe String. Sem dúvida ela é uma das principais classes da linguagem Java, como todos sabem, responsável por armazenar e manipular dados do tipo texto. Porém, existem características importantes desta classe que são desconhecidas, ou ignoradas, por alguns.Existem diversos fóruns e blogs que falam sobre performance, concatenação, capacidade, sobre a imutabilidade (ou mutabilidade, por reflexão), e outras características sobre a Classe String. Neste artigo nos direcionaremos sobre o mais importante, a sua correta utilização. Alguns falarão: Mas isso é muito básico, todos sabem disso. Porém não é o que encontramos no dia-a-dia. Nos mais diferentes tipos aplicação, sempre nos deparamos com códigos como:

Listagem 1: Classe TestString


public class TestString {

	public static void main(String[] args) {
		
        String str ="something"; 

        str.concat(" else");

        System.out.println(str);
	
}

Inicialmente, na linha 3, é criada uma String com o conteúdo "something". Na linha 4 é criada uma nova String concatenada pelo método concat(), com conteúdo " something else ". A referência da variável str neste momento aponta para a String com conteúdo "something else". As Strings "something" e "else" estão sem referência, ou seja, estão disponíveis para o Garbage Collector.

Alguns perguntarão: Mas está errado? Não. Na realidade, na área de desenvolvimento de software sempre temos que ter em mente que podem existir diversas soluções para um mesmo problema. Mas existe um ponto crucial que deve ser considerado, o problema de se manipular um tipo de dado texto diretamente com a classe String quando utilizamos um grande volume de dados. Ao se trabalhar diretamente com a classe String, muitos objetos do tipo String são criados para se conseguir um valor desejado. A listagem 2 mostra um loop onde são realizadas 50 mil iterações, e a cada iteração ocorre a concatenação com o valor obtido na iteração anterior.

Listagem 2: Classe TestLoopString


public class TestLoopString {
	
	public static void main(String[] args) {
		
		double t1;
		double t2;
		
		String str = "";
		
		t1 = System.currentTimeMillis();
		
		for (int i = 0; i<50000; i++) {
		        str += i;
		}
		
		t2 = System.currentTimeMillis();
		
		System.out.println("Tamanho String: "+ str.length());
		System.out.println("Tempo Gasto: "+ (t2 - t1));

	}
}

Este exemplo é curioso, principalmente no ponto onde concatenamos a instância str à variável i, visto pela expressão str += i. Você poderá dizer: O tempo de resposta não foi ruim, visto a grande quantidade de iterações sofridas. Realmente não, mas isso tem uma explicação. É agora que vem a parte interessante. Simplificadamente, a JVM não reconhece o operador +, único operador de overload em Java. Este operador usa por “debaixo do panos” a classe StringBuilder, que será vista logo a seguir.

Comparativo entre String, StringBuffer e StringBuilder

Figura 1: Comparativo entre String, StringBuffer e StringBuilder

A classe StringBuilder geralmente é utilizada quando precisamos armazenar e manipular um grande número de caracteres. Esta classe tem como principal característica, o fato de ser um objeto mutável, ou seja, o seu valor pode ser alterado. Isto permite que o valor armazenado em sua instância possa ser alterado, principalmente utilizando-se o método append(). Outra característica importante desta classe é que ela é final, ou seja, não pode ser herdada por nenhuma outra classe. Existe ainda uma classe muito parecida com StringBuilder, mas com algumas particularidades importantes. Esta classe é a StringBuffer. A principal diferença entre essas duas classes é que StringBuffer, por ter todos seus métodos synchronized, é usada em cenários multi-thread, não necessitando ser utilizada na maioria dos casos. A listagem 3 mostra como a JVM interpreta o operador de overload +:

Listagem 3: Classe TestLoopString vista pela JVM


public class TestLoopString {
	
	public static void main(String[] args) {
		
		double t1;
		double t2;
		
		String str = "";
		
		t1 = System.currentTimeMillis();
		
		for (int i = 0; i<50000; i++) {
			
		        str = new StringBuilder().append(str).append(i).toString();

		}
		
		t2 = System.currentTimeMillis();
		
		System.out.println("Tamanho String: "+ str.length());
		System.out.println("Tempo Gasto: "+ (t2 - t1));

	}
}

Na linha 8, podemos observar como a JVM interpreta o operador + usado para a concatenação. Você irá perguntar: Mas sempre precisamos criar um novo StringBuider para cada iteração? Não. A cada iteração um novo objeto do tipo StringBuilder é criado, e é realizada a concatenação da variável str com a variável de incremento i. A criação de um objeto por iteração, além de ocupar espaço desnecessário em memória, prejudica a performance da aplicação. O mais interessante seria que uma única instância do objeto StringBuilder fosse compartilhada por nossa aplicação, e utilizada durante a iteração. O código com essa alteração é mostrado na listagem 4.

Listagem 4: Classe TestLoopString


public class TestLoopString {
	
	public static void main(String[] args) {
		
		double t1;
		double t2;
		
		t1 = System.currentTimeMillis();
		
		StringBuilder str = new StringBuilder();
		
		for (int i = 0; i<50000; i++) {
			
			str.append(i);
			
		}
		
		t2 = System.currentTimeMillis();
		
		System.out.println("Tamanho String: "+ str.toString().length());
		System.out.println("Tempo Gasto: "+ (t2 - t1));

	}
}

Agora entende porque não é tão simples utilizar sempre a classe String? Para se ter uma compreensão melhor do que ocorre, e comparar os tempos de resposta das alterações, execute o código da listagem 3 e depois execute o código da listagem 4, e veja por si mesmo a diferença encontrada. Em um computador com core I3, 4 GB de RAM, foram obtidos os seguintes resultados:

  • Tamanho String: 238890
  • Tempo Gasto: 37097.0
  • Tamanho String: 238890
  • Tempo Gasto: 12.0

Bom pessoal, por hoje é isso. Espero que tirem proveito deste artigo para otimizarem seus códigos e ensinarem os amigos, mas lembre-se, sempre com moderação. Abraços.