Otimização de código

Oi,

Um colega de trabalho, perguntou qual a diferenca e qual situação usar para comparar uma String… se vazia ou se existir conteudo.

situação 1:

if ( !string.equals("") )

ou

situação 2:

if ( string.lenght() > 0 )

Então, respondi a segunda opção, porém não souber explicar o motivo hehehe…
algum comentario a respeito?

Tchauzin!

Com a primeira opção vc estará criando uma String a mais na memória.
Já com a segunda é apenas uma comparação com um tipo primitivo.

equals() compara o conteudo da String e == compara a referencia.

De qualquer forma, tem o compareTo e compareToIgnoreCase.

O length é mais rápido por se tratar apenas da leitura de um atributo e da comparação no seu if, observe (esses códigos foram retirados da classe String):

public int length() {
        return count;
    }

No caso do equals, nessa situação específica, você vai ter um processo lijeiramente mais longo, pois serão feitas três comparações. A primeira, testa se a string passada no parâmetro é a própria instância (o que nunca vai ocorrer). A segunda, testa se o objeto passado por parâmetro é realmente um String (o que sempre vai ser). E a terceira, finalmente, testa se o tamanho das Strings bate (o que só será verdade para um string vazio).

public boolean equals(Object anObject) {
   if (this == anObject) { //Comparação 1
      return true;
   }
   if (anObject instanceof String) {  //Comparação 2
      String anotherString = (String)anObject;    
      int n = count;
      if (n == anotherString.count) {    //Comparação 3
         char v1[] = value;
         char v2[] = anotherString.value;
         int i = offset;
         int j = anotherString.offset;
         while (n-- != 0) {
            if (v1[i++] != v2[j++])
               return false;
      }
      return true;
   }
}
	return false;
    }

Usando o equals é ainda mais recomendado colocar a constante na frente, assim:

if (!"".equals(string))

Isso dá uma vantagem adicional, pois o código também falha caso a string seja nula. Nos seus exemplos, você acabaria com um NullPointerException.

Agora, não creio que isso seja otimização de código, a menos que você esteja num loop realmente intenso, comparando milhares de Strings por segundo. Provavelmente haverá outros locais muito piores na sua aplicação. Não adianta “otimizar” pontos fora desses gargalos. Você só aumentará o tempo ocioso do sistema, que espera o gargalo passar. Sempre que se falar em otimização, rode um profiler, como o do Netbeans, e identifique os pontos de baixa performance. Otimize apenas esses pontos.

Otimização tem um custo: ela torna o código mais complexo e muito menos flexível. E deve ser uma preocupação apenas nos momentos de projeto e nos pontos críticos identificados no código.

[quote=Foxlol]Com a primeira opção vc estará criando uma String a mais na memória.
Já com a segunda é apenas uma comparação com um tipo primitivo.[/quote]

É muito provável que essa String venha do Pool de Strings. Não lembro onde li, mas creio que a String nula sempre esteja por lá.

Respondendo, o == é mais rapido, tinha esquecido :slight_smile:

Explicando o por que:

O Java suporta algo chamado de “interning”. Quando o metodo intern() é invocado em uma String, é feito um lookup em uma tabela interna de Strings. Se uma String com o mesmo conteudo já está nesta tabela, a referencia para ela e retornada, do contrario, a String é adicionada a tabela e a referencia a ela é retornada.

No fim, este processo faz com que todas as Strings com o mesmo conteudo apontem para o mesmo objeto, salvando espaço e permitindo a comparação com ==, que é muito mais rapida do que o equals, como o vinny explicou.

Referencia: http://javatechniques.com/blog/string-equality-and-interning/

Realmenta. Mas isso não serve para esse tipo de comparação que a Lina propôs.
Strings de conteúdo igual não são necessariamente iguais e dificilmente podemos afirmar que a String na variável veio de uma constante ou teve o seu intern() invocado.

Teste você mesmo:

String x = "Vinicius";
String y = new String("Vinicius");

if (x == y)
   System.out.println("É igual");
else
   System.out.println("É diferente");

Strings lidas de bancos de dados, ou capturadas de um JTextField não entram nesse caso.
É exatamente o alerta que o artigo dá no final.

Em todo caso, comparar com == envolve uma comparação de endereços de memória, que é um int. Enquanto com o length() envolve a comparação com um inteiro, que também é um int. A performance dos dois é provavelmente a mesma. :wink:

E como vc garante que o conteudo é o mesmo com o length? Já que ele só compara o tamanho.

Provavelmente você terá que fazer uma comparação posterior, que somada deixara o processo mais lento.

Ah! Deixa pra la, acabei perdendo o foco do topico que era a comparação do equal com o lenght :stuck_out_tongue:

Realmenta. Mas isso não serve para esse tipo de comparação que a Lina propôs.
Strings de conteúdo igual não são necessariamente iguais e dificilmente podemos afirmar que a String na variável veio de uma constante ou teve o seu intern() invocado.

Teste você mesmo:

String x = "Vinicius";
String y = new String("Vinicius");

if (x == y)
   System.out.println("É igual");
else
   System.out.println("É diferente");

Strings lidas de bancos de dados, ou capturadas de um JTextField não entram nesse caso.
É exatamente o alerta que o artigo dá no final.

Em todo caso, comparar com == envolve uma comparação de endereços de memória, que é um int. Enquanto com o length() envolve a comparação com um inteiro, que também é um int. A performance dos dois é provavelmente a mesma. ;)[/quote]

Bah… que macabro, essas 2 Strings são diferentes mesmo…

Mas se for para testar se está vazia ou não, o lenght é melhor.

Retirado do livro da Kathy

Não garante. Mas o objetivo era testar se a String era vazia. Nesse caso, com certeza o length é 0.

Não adianta… se precisar comparar duas Strings, ou você terá que usar o equals da classe String, ou o equals da classe Collator (caso queira, por exemplo, desprezar os acentos).

[quote=ViniGodoy][quote=Foxlol]Com a primeira opção vc estará criando uma String a mais na memória.
Já com a segunda é apenas uma comparação com um tipo primitivo.[/quote]

É muito provável que essa String venha do Pool de Strings. Não lembro onde li, mas creio que a String nula sempre esteja por lá.[/quote]

Isso, toda constante string fica na tabela de símbolos do interpretador. Será “desperdício” de memória se essa constante aparecer uma única vez na aplicação.

Flw

Oi,

vlw pelas respostas!

Tchauzin!

[quote=daniel_s]…
Isso, toda constante string fica na tabela de símbolos do interpretador. Será “desperdício” de memória se essa constante aparecer uma única vez na aplicação.
Flw[/quote]

Daniel,

em tese o próprio paradigma orientado objeto tem esse efeito colateral (de desperdício de memória), mas em função da grande necessidade de ponteiros associativos…

Com relação ao cache interno de Constantes String do java e elembrando que um objeto String é “imutável”, a equipe da SUN quando escrever essa parte da especificação estava em mente com a seguintes situação:

String a = “SACANA”;
String b = “CANA”;
String c = “ANA”;

Neste caso seriam necessários os famosos ponteiros para os endereçõs de memória mais 16 bytes para a informação texto (lembrando que cada String tende a ter um terminador…). Com o Cache intern o java faz com que as mesmas String ocupem apenas 7 bytes. Economia de mais de 50% neste exemplo.
A Lógica vem de que a String é “imutável”. O ponteiro de a, supomos inicia na posição 1000, o ponteiro de b inicia na posição 1002 e a de c na posição 1003.

E até onde eu me lembro, o cache é implementado segundo o conceito de LRU (http://en.wikipedia.org/wiki/Cache_algorithms).

fw

No caso de variáveis sim, estava referenciando constantes. Essas são tratadas de forma diferente:

[code]String a = “ANA”;
“SACANA”.equals(a);

(new StringBuilder()).append(a).append(“SACANA”);[/code]

A constante “SACANA” é tratada como uma única instância String.

http://java.sun.com/docs/books/jvms/second_edition/html/VMSpecTOC.doc.html

“(…) The Java programming language requires that identical string literals (that is, literals that contain the same sequence of characters) must refer to the same instance of class String (…)”

Abraços

De qualquer maneira, a principal otimização que pode ser feita com strings é o uso de StringBuffer (<= 1.4) ou StringBuilder (>= 5.0) quando for possível e razoável.
O uso do StringBuilder, no entanto, deve ser feito corretamente para evitar código incompreensível, já que normalmente só vale a pena se houver mais de 5 ou 6 concatenações numa mesma string, e essas concatenações não puderem ser resolvidas com o operador “+” em uma mesma expressão. Ou seja, é melhor você usar:

String sql = "INSERT "
   + "INTO  "
   + nomeTabela
   + " "
   + " ( " 
   + campo1 
   + ","
   + ....;

que isto aqui:

// Evitar
String sql = "INSERT "
   sql += "INTO  ";
   sql += nomeTabela;
   sql += " ";
   sql += " ( " ;
   sql += campo1 ;
   sql += ",";
   sql += ...

ou isto aqui:

// Relativamente rápido, mas incompreensível
StringBufferl sbSql = new StringBuffer();
   sbSql.append ("INSERT ");
   sbSql.append ("INTO  ");
   sbSql.append (nomeTabela);
   sbSql.append (" ");
   sbSql.append (" ( ") ;
   sbSql.append (campo1) ;
   sbSql.append (",");
   sbSql.append (...
   String sql = sbSql.toString();

Isso sem falar no clássico erro:

StringBuilder sb = new StringBuilder(); sb.append("E os valores são: " + var1 + var2 + var3 + var4 + var5 + var6); return sb.toString();

Vocês não tem idéia de quantas vezes já vi isso em códigos por aí…

[quote=ViniGodoy]Isso sem falar no clássico erro:

StringBuilder sb = new StringBuilder(); sb.append("E os valores são: " + var1 + var2 + var3 + var4 + var5 + var6); return sb.toString();

Vocês não tem idéia de quantas vezes já vi isso em códigos por aí… [/quote]

Hehe, isso é comum mesmo. Muitas vezes você faz e nem percebe. Só no dia seguinte que você vê e tenta imaginar no que você estava pensando quando escreveu isso.

como Martin Fowler diz na primeira vez vc apenas digita, na segunda vc melhora e na terceira vc refatora :slight_smile:

Não. “abc” e “def” têm o mesmo length. Só se o tamanho é igual é que se tem que passar a uma analise caracter a caracter. Os outros testes são otimizações. O segundo teste tb teste o null. Nem sempre vai ser uma string

Comfronte com isto:

// compra se duas coleções são iguais.
// elas são iguais se contêm os mesmos itens
public boolean equals ( Collection a , Collection b){
     if ( a == b ){ // null == null 
          return true;
     }  

     if (a.size()!=b.size()){
        return false;
     } 
     
     // compara item a item.
// so posso fazer deta forma porque
// sei que o numero de elementos é o mesmo
     for (Iterator it = a.iterator();it.hasNext();){
          if (!b.contains(it.next)){
                return false;
          }
     }
     return true;
}