Multiplos de 3 de 1 a 100

Ola pessoal estou a estudar as apostilas da caelum e tou com duvidas neste exercicio que axo que nao fiz da melhor maneira e agradecia que me explicassem melhor se possivel obrigado.

class MultiplosTres {
	public static void main(String args[]){
	     int i;
	     
	     for (i = 1; i < 100; i++){
	      i = i + 2;
            
	     	System.out.println(i);
	     }
	     	
	}
}

Você quer um algoritmo melhor para imprimir os múltiplos de 3 entre 1 e 100?
Eu usaria módulo.

if(i%3==0){
				System.out.println("Numero " + i +" é multiplo de 3 ");
			} 

O código do rdgc é o mais eficiente. Por módulo tb está correto, mas vc faz mais cálculos e ainda tem um if. Não q seja o fim do mundo, mas só p vc entender.

[size=18] Nem tudo é como parecer ser![/size]

Se vocês pretendem deixar o código otimizado ao máximo, devem fazer apenas uma chamada de sistema operacional para imprimir na tela. Para isso precisa armazenar o resultado em memória para depois imprimir apenas uma vez. Utilizar String para isso é inviável em termos de performance, pois ela é imutável. StringBuffer é a classe eficaz para concatenar muitas Strings, pois ela é mutável. StringBuilder faz a mesma coisa, só que previne a concatenação ao objeto de duas ou mais Threads, nesse exemplo não é nececessária pois existe só a Thread padrão que executa o método main().

Eis o código:public static void main(String args[]){ long t1, t2; t1 = System.currentTimeMillis(); StringBuffer sb = new StringBuffer(); String quebraLinha = "\n"; for (int i = 3; i < 10000; i=i+3){ sb.append(i + quebraLinha); } System.out.println(sb.toString()); t2 = System.currentTimeMillis(); System.out.println(t2-t1 + " milisegundos."); }Para computar e imprimir todos múltiplos de 3 de 1 até 10.000 levou-se 234 milisegundos.

Para aquele código inicial:public static void main(String args[]){ long t1, t2; t1 = System.currentTimeMillis(); int i; for (i = 1; i < 100; i++){ i = i + 2; System.out.println(i); } t2 = System.currentTimeMillis(); System.out.println(t2-t1 + " milisegundos."); }Para computar e imprimir todos múltiplos de 3 de 1 até 10.000 levou-se 953 milisegundos.

Para aquele código incrementando de três em três:public static void main(String args[]){ long t1, t2; for (int i = 3; i < 10000; i = i + 3) { System.out.println("Múltiplo de 3: " + i); } t2 = System.currentTimeMillis(); System.out.println(t2-t1 + " milisegundos."); }Para computar e imprimir todos múltiplos de 3 de 1 até 10.000 levou-se 938 milisegundos.

Claro que esses tempos podem variar de acordo com a preempção da Thread que executa o código. Mas pelo que vocês podem ver, imprimir na tela uma vez sequer é muito, muito, muito mais lento do que uma simples otimização de incremento.

Sua mensagem está meio certa. Primeiro:

  • Sim, fazer uma só chamada ao SO torna o seu código mais rápido.
  • Agora é quase que falácia dizer que concatenar Strings é mais lento que usar StringBuffer/StringBuilder. Quer saber por que? Aprenda a usar o javap.

Até!

[quote=maquiavelbona]

  • Agora é quase que falácia dizer que concatenar Strings é mais lento que usar StringBuffer/StringBuilder.[/quote]

Se for quase que falácia, então me explique pq esse código com StringBuffer roda em 47ms.
long t1, t2; t1 = System.currentTimeMillis(); StringBuffer s = new StringBuffer(); for (int i = 0; i < 10000; i++) { //47ms s.append(i + " "); } t2 = System.currentTimeMillis(); System.out.println(s); System.out.println(t2-t1 + "ms.");
E me explique pq esse código abaixo que concatena strings roda em 11422ms.
long t1, t2; t1 = System.currentTimeMillis(); String s = ""; for (int i = 0; i < 10000; i++) { //11422ms s += i + " "; } t2 = System.currentTimeMillis(); System.out.println(s); System.out.println(t2-t1 + "ms.");
Como se vê, é concatenação em Strings versus concatenação em StringBuilder.

Ainda não enxerguei nenhuma falácia no que eu disse.

:smiley:

Você trocou as bolas ao explicar do StringBuffer e do StringBuilder nesse caso. O StringBuffer é sincronizado e previne o acesso concorrente, e o StringBuilder, não. Portanto, corrija os seus exemplos para usar o StringBuilder, não o StringBuffer. Veja o que diz a documentação do StringBuffer:
“As of release JDK 5, this class has been supplemented with an equivalent class designed for use by a single thread, StringBuilder. The StringBuilder class should generally be used in preference to this one, as it supports all of the same operations but it is faster, as it performs no synchronization.”

Na prática, raramente você utilizará o StringBuffer, pois o acesso concorrente a um único buffer raramente faz sentido. A sincronização só previne que o método append() não seja chamada por duas threads simultâneas, porém, ela não garante nada sobre o que acontece entre duas chamadas ao método append. Por exemplo, se o código
strBuf.append("15/09/1980 ").append("log line ");

For executado por duas threads diferentes a saída poderá ser “15/09/1980 log line 15/09/1980 log line” ou poderá ser “15/09/1980 15/09/1980 log line log line”. O que ela não poderá ser (já que o StringBuffer é sincronizado) é "15log/09/li19ne80 15log/09/li19ne80 ".

A solução para isso é sincronizar a classe que contém o StringBuffer, mas nesse caso, a sincronização do buffer em si torna-se desnecessário, e podemos substitui-lo por um stringBuilder.

A concatenação de Strings realmente é mais rápida com essas classes. Ela só não é mais rápida em casos assim:

String nomeCompleto = titulo + nome + sobrenome + complemento;

Que o java irá substituir invariavelmente por:

String nomeCompleto = new StringBuilder(titulo).append(nome).append(sobrenome).append(complemento);

Mas, realmente, dentro de um while o tempo é incomparavelmente menor, como você mesmo demonstrou.

Outra coisa que eu notei…

Já que você está usando o StringBuilder, não use a contatenação dentro do método append, foi feito na linha 7 do programa:

sb.append(i + quebraLinha);

Dessa forma, você pode voltar a incorrer nos custos que queria evitar. No lugar, use o método append do próprio StringBuilder. Uma característica interessante é que esse método retorna o próprio StringBuilder, o que permite-nos chamar em cadeia:

sb.append(i).append(quebraLinha);

Melhor que isso, só se a Sun tivesse escolhido a palavra add no lugar de append…

Você trocou as bolas ao explicar do StringBuffer e do StringBuilder nesse caso. O StringBuffer é sincronizado e previne o acesso concorrente, e o StringBuilder, não. Portanto, corrija os seus exemplos para usar o StringBuilder, não o StringBuffer. Veja o que diz a documentação do StringBuffer:
“As of release JDK 5, this class has been supplemented with an equivalent class designed for use by a single thread, StringBuilder. The StringBuilder class should generally be used in preference to this one, as it supports all of the same operations but it is faster, as it performs no synchronization.”

Na prática, raramente você utilizará o StringBuffer, pois o acesso concorrente a um único buffer raramente faz sentido. A sincronização só previne que o método append() não seja chamada por duas threads simultâneas, porém, ela não garante nada sobre o que acontece entre duas chamadas ao método append. Por exemplo, se o código
strBuf.append(“1”).append(“2”);

For executado por duas threads diferentes a saída poderá ser 1212 ou poderá ser 1122.

A solução para isso é sincronizar a classe que contém o StringBuffer, mas nesse caso, a sincronização do buffer em si torna-se desnecessário, e podemos substitui-lo por um stringBuilder.

A concatenação de Strings realmente é mais rápida com essas classes. Ela só não é mais rápida em casos assim:

String nomeCompleto = titulo + nome + sobrenome + complemento;

Que o java irá substituir invariavelmente por:

String nomeCompleto = new StringBuilder(titulo).append(nome).append(sobrenome).append(complemento);

Mas, realmente, dentro de um while o tempo é incomparavelmente menor, como você mesmo demonstrou.[/quote]

Não troquei as bolas não.

Evitar o acesso concorrente para concatenação no objeto é a mesma coisa que evitar a concatenação ao objeto de duas ou mais Threads ( ao mesmo tempo! esqueci de escrever isso… :stuck_out_tongue: )

[quote=ViniGodoy]Outra coisa que eu notei…

Já que você está usando o StringBuilder, não use a contatenação dentro do método append, foi feito na linha 7 do programa:

sb.append(i + quebraLinha);

Dessa forma, você pode voltar a incorrer nos custos que queria evitar. No lugar, use o método append do próprio StringBuilder. Uma característica interessante é que esse método retorna o próprio StringBuilder, o que permite-nos chamar em cadeia:

sb.append(i).append(quebraLinha);

Melhor que isso, só se a Sun tivesse escolhido a palavra add no lugar de append…[/quote]

Com certeza, eu até iria fazer isso, mas daí ficaria diferente do programa anterior, pois eu queria adicionar strings propriamente dito.

Ué, se não trocou as bolas, porque seu código está com o StringBuffer, e não o StringBuilder, quando a documentação diz que é justamente o contrário que se deve fazer?

Sua explicação está trocada sim cara. Leia o Javadoc.

[quote=ViniGodoy]Ué, se não trocou as bolas, porque seu código está com o StringBuffer, e não o StringBuilder, quando a documentação diz que é justamente o contrário que se deve fazer?

Sua explicação está trocada sim cara. Leia o Javadoc.[/quote]

Viajei total! hauehuaehuea
É verdade, troquei as bolas…
Se bem que quem nomeou essas classes poderia colocar algo mais sugestivo…

Valeu!

[quote=dionat4n]…

Ainda não enxerguei nenhuma falácia no que eu disse.[/quote]
Javap -verbose do trecho do código usando String:

   16:	iload	6
   18:	sipush	10000
   21:	if_icmpge	56
   24:	aload	5
   26:	new	#5; //class java/lang/StringBuilder
   29:	dup
   30:	invokespecial	#6; //Method java/lang/StringBuilder."<init>":()V
   33:	iload	6
   35:	invokevirtual	#7; //Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
   38:	ldc	#8; //String  
   40:	invokevirtual	#9; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
   43:	invokevirtual	#10; //Method java/lang/StringBuilder.toString:()Ljava/lang/String;
   46:	invokevirtual	#11; //Method java/lang/StringBuffer.append:(Ljava/lang/String;)Ljava/lang/StringBuffer;
   49:	pop
   50:	iinc	6, 1
   53:	goto	16

Agora o trecho do código usando StringBuilder.

   11:	iload	6
   13:	sipush	10000
   16:	if_icmpge	52
   19:	new	#4; //class java/lang/StringBuilder
   22:	dup
   23:	invokespecial	#5; //Method java/lang/StringBuilder."<init>":()V
   26:	aload	5
   28:	invokevirtual	#6; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
   31:	iload	6
   33:	invokevirtual	#7; //Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
   36:	ldc	#8; //String  
   38:	invokevirtual	#6; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
   41:	invokevirtual	#9; //Method java/lang/StringBuilder.toString:()Ljava/lang/String;
   44:	astore	5
   46:	iinc	6, 1
   49:	goto	11

Agora vamos com as considerações:
-testes de microscópio nada inferem. Sua máquina poderia estar rodando algo em algum momento;
-você viu em algum lugar que o código concatena Strings diretamente? Ou será que o compilador foi esperto o suficiente para saber que tinha um loop concatenando String?

Até!

Eu executei diversas vezes o mesmo código para me certificar disso. Se duvida, faça isso.

Nesse link abaixo esclarece tudo:
“In conclusion, StringBuffer concatenation is significantly faster than String concatenation. Obviously, StringBuffers should be used in this type of operation when possible. If the functionality of the String class is desired, consider using a StringBuffer for concatenation and then performing one conversion to String.”
http://www.javaworld.com/javaworld/jw-03-2000/jw-0324-javaperf.html

http://nicklothian.com/blog/2005/06/09/on-java-string-concatenation/

http://java.sun.com/developer/JDCTechTips/2002/tt0305.html

http://www.precisejava.com/javaperf/j2se/StringAndStringBuffer.htm

Os artigos se contradizem…

Obrigado pela insistência, na próxima terei que analisar melhor isso.

[quote=dionat4n]Eu executei diversas vezes o mesmo código para me certificar disso. Se duvida, faça isso.

Nesse link abaixo esclarece tudo:
“In conclusion, StringBuffer concatenation is significantly faster than String concatenation. Obviously, StringBuffers should be used in this type of operation when possible. If the functionality of the String class is desired, consider using a StringBuffer for concatenation and then performing one conversion to String.”
http://www.javaworld.com/javaworld/jw-03-2000/jw-0324-javaperf.html[/quote]

Em 2000 isso era verdade.

Hoje em 2009, muita coisa foi otimizada no Java 6, concatenação de Strings é uma delas. Não há mais tanta diferença.

[quote=Bruno Laturner][quote=dionat4n]Eu executei diversas vezes o mesmo código para me certificar disso. Se duvida, faça isso.

Nesse link abaixo esclarece tudo:
“In conclusion, StringBuffer concatenation is significantly faster than String concatenation. Obviously, StringBuffers should be used in this type of operation when possible. If the functionality of the String class is desired, consider using a StringBuffer for concatenation and then performing one conversion to String.”
http://www.javaworld.com/javaworld/jw-03-2000/jw-0324-javaperf.html[/quote]

Em 2000 isso era verdade.

Hoje em 2009, muita coisa foi otimizada no Java 6, concatenação de Strings é uma delas. Não há mais tanta diferença.[/quote]

Tem razão… Mas o que rodei aqui foi no Java 6, estranho.