Otimização de código

e que tal isto aqui :

// rápido, mas compreensível
StringBuffer sbSql = new StringBuffer()
   .append ("INSERT INTO  ")
   .append (nomeTabela)
   .append (" ( ") 
   .append (campo1) 
   .append (",")
   .append (...

   String sql = sbSql.toString();

StringBuilder/Buffer é o cara. Usar + é gamb.

[quote=sergiotaborda][quote=ViniGodoy]
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).
[/quote]
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[/quote]
Ué, mas ele não disse que você terá uma comparação caractere a caractere.

[quote=Schuenemann][quote=sergiotaborda][quote=ViniGodoy]
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).
[/quote]
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[/quote]
Ué, mas ele não disse que você terá uma comparação caractere a caractere.[/quote]

??

Você disse “Não” e “Só se o tamanho é igual é que se tem que passar a uma analise caracter a caracter”, sendo que isto não foi dito. O que você negou, afinal?

Nem sempre vai ser : null.

Não é verdade “que só será verdade para um string vazio”. Dai o exemplo de “abc” e “def”. O teste é feito porque se o tamnho das strings é diferente com certeza elas são diferentes, mas se os tamanhos são iguais tem que analizar caracter a carater. Dai a comparação com a analize que se faz para coleções ( afinal um String é uma coleção de char)

[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]

Esse codigo nao tem problema, o compilador é esperto e substitui pelo uso de StringBuilder.
O problema de usar + em concatenacao de String é em geral só dentro de laco.

[quote=sergiotaborda]

// rápido, mas compreensível
StringBuffer sbSql = new StringBuffer()
   .append ("INSERT INTO  ")
   .append (nomeTabela)
   .append (" ( ") 
   .append (campo1) 
   .append (",")
   .append (...

   String sql = sbSql.toString();

StringBuilder/Buffer é o cara. Usar + é gamb. [/quote]

Oi Sergio. Esse é outro caso que usar + não seria gambi: como esta fora de um laco, é muito facil e o compilador detecta que o StringBuilder aqui poderia ter sido usado totalmente sem problemas. Alias, como ele usaria StringBuilder, esse código abaixo é mais rapido que o seu: :slight_smile:

// rápido, mas compreensível
String sql = "INSERT INTO  " + nomeTabela + " ( " + campo1 + ",";

Early optimization is the root of all evil…

Isso era verdade antes do Java 6, o tratamento de Strings foi otimizado nesta versão.

Porém, ainda há um caso em que StringBuffer/Builder é melhor:

StringBuffer buffer = new StringBuffer(NUMERO_DE_CARACTERES_ESPERADO);

Esse número tem que ser perfeitamente suficiente p/ que StringBuffer não aloque mais memória durante a execução.

Isso era verdade antes do Java 6, o tratamento de Strings foi otimizado nesta versão.
[/quote]

Mesmo assim, ainda nao deve ser usado para concatenar dentro de um laco se a string que voce esta mexendo esta referenciada fora do laco! Ele geraria milhares de StringBuilders, estressando o GC. Fica uma carroca

Aí não tem versão de Java que resolva né? É problema de estrutura de código, não da linguagem.

Aí não tem versão de Java que resolva né? É problema de estrutura de código, não da linguagem.[/quote]

Perfeito, isso mesmo.

Peraee!!!

Dexa eu ver se eu entendi ou to boiando.

Quer dizer que depois do Java 6, ao usar “+” para concatenar String, o compilador substitui automaticamente para StringBuilder?

O.o

[quote=Foxlol]Peraee!!!

Dexa eu ver se eu entendi ou to boiando.

Quer dizer que depois do Java 6, ao usar “+” para concatenar String, o compilador substitui automaticamente para StringBuilder?

O.o[/quote]

Desde o Java 1.0 é usando o StringBuffer.

O problema é que a primeira vez ele da new em String Buffer. Se estiver dentro de um loop, ja viu… Qual eh a solucao? Dar new FORA do laco, e dentro so chamar append. Mas isso ai o compilador nao faz.

[quote=Paulo Silveira][quote=Foxlol]Peraee!!!

Dexa eu ver se eu entendi ou to boiando.

Quer dizer que depois do Java 6, ao usar “+” para concatenar String, o compilador substitui automaticamente para StringBuilder?

O.o[/quote]

Desde o Java 1.0 é usando o StringBuffer.

O problema é que a primeira vez ele da new em String Buffer. Se estiver dentro de um loop, ja viu… Qual eh a solucao? Dar new FORA do laco, e dentro so chamar append. Mas isso ai o compilador nao faz.
[/quote]

Eita, não sabia dessa!

Então dá pra concatenar Strings com “+” sem problemas, desde que não esteja em um loop. Ai tem que tomar este cuidade que vc falou.

Vlw

[quote=sergiotaborda][quote=ViniGodoy]
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).
[/quote]
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
[/quote]

Sérgio, estou falando do teste:

!string.equals("")

Que foi proposto pela Lina, no início do tópico. Não tem absolutamente nada a ver com coleções.

E, como mostrado no código fonte da classe String (está no início do tópico), são feitas 3 comparações, sendo 2 delas desnecessárias.

A primeira, verifica se o objeto recebido no parâmetro e o this são exatamente o mesmo objeto. No caso desse exemplo, as chances disso ocorrer são ridiculamente pequenas.

O segundo teste, verifica se o objeto recebido é realmente uma String. Da forma como a Lina usa isso sempre vai ser verdade. “” nunca será nulo e sempre será uma String. Não estou criticando o mérito desse teste, no caso de um método equals de verdade, ele é necessário. Mas o que ela quer não é testar igualdade, mas se um determinado String está vazio ou não.

Só então, é feito um cast, uma atribuição a uma variável “n” e uma comparação em para só então ocorrer a comparação com o tamanho, que você falou. Por favor, leia o tópico com mais atenção da próxima vez. Aliás, em momento algum falei sobre ser mais lento por haver comparação caracter a caracter.

A forma que eu recomendei foi o simples de tamanho == 0. Essa forma é, sim, mais rápida, já que poupa os dois testes iniciais que só são necessários e servem de otimização no caso do equals.

Mas, como eu ressaltei e volto a ressaltar, não adianta otimizar código se você não estiver num ponto que constitui um gargalo em sua aplicação.

[code]package testes;

public class TesteStrings {

private static final long ITERACOES = 100*1000*1000;
private static final String TESTE_VAZIO = "";
private static final String TESTE_NAO_VAZIO = "        ";
    
public static void main (String[] args)
{
    teste(TESTE_VAZIO, 10, ITERACOES);
    teste(TESTE_NAO_VAZIO, 10, ITERACOES);
}

private static void teste(String s, int loops, long iter)
{
    long r1, r2, r3, r4;

    r1 = r2 = r3 = r4 = 0;
    
    for (int n = 0; n < loops; n++)
    {
        r1 += testeLength(s, iter);
        r2 += testeEquals1(s, iter);
        r3 += testeEquals2(s, iter);
        r4 += testeIsEmpty(s, iter);
        
    }
    
    System.out.println("String \"" + s + "\"");
    System.out.println("Média em " + loops + " loops:");
    System.out.println("s.length()   " + r1/loops);
    System.out.println("s.equals(\"\") " + r2/loops);
    System.out.println("\"\".equals(s) " + r3/loops);
    System.out.println("s.isEmpty()  " + r4/loops + "\n");
}

private static long testeLength(String s, long iteracoes)
{
    long t1, t2, i;
    
    t1 = System.currentTimeMillis();
    for (i = 0; i < iteracoes; i++)
        if (s.length() == 0);
    t2 = System.currentTimeMillis();
    
    return t2 - t1;        
}

private static long testeEquals1(String s, long iteracoes)
{
    long t1, t2, i;
    
    t1 = System.currentTimeMillis();
    
    for (i = 0; i < iteracoes; i++)
        if (s.equals(""));
    
    t2 = System.currentTimeMillis();
    
    return t2 - t1;        
}

private static long testeEquals2(String s, long iteracoes)
{
    long t1, t2, i;
    
    t1 = System.currentTimeMillis();
    
    for (i = 0; i < iteracoes; i++)
        if ("".equals(s));
    
    t2 = System.currentTimeMillis();
    
    return t2 - t1;        
}

private static long testeIsEmpty(String s, long iteracoes)
{
    long t1, t2, i;
    
    t1 = System.currentTimeMillis();
    
    for (i = 0; i < iteracoes; i++)
        if (s.isEmpty());
    
    t2 = System.currentTimeMillis();
    
    return t2 - t1;        
}

}[/code]

100 milhões de iterações, em milissegundos:

[code]
String vazia
Média em 10 loops:
s.length() 317
s.equals("") 993
“”.equals(s) 1022
s.isEmpty() 390

String não vazia
Média em 10 loops:
s.length() 351
s.equals("") 1396
“”.equals(s) 1521
s.isEmpty() 496[/code]

Edit 2: Corrigido o benchmark
Edit 3: Refatorado o benchmark

Editei o post acima.

Tem algo errado nesse seu benchmark.

Veja só, o método isEmpty está implementado assim:
return count == 0;

Isso é exatamente o que faz o seu teste com length.
Alguém saberia explicar pq ele tem a mesma performance do equals?

De qualquer forma, ele vem a provar que a performance desses métodos é tão boa, que dificilmente eles ocasionariam problemas de performance para essa situação.

Erro na linha 16!

System.out.println("s.isEmpty()  " + testeEquals2(TESTE, ITERACOES) + "\n");  

Você está chamando o método testeEquals2 e não o testeIsEmpty…

Corrigi o benchmark. Agora ele faz só médias.