Hospedagem Profissional

Hospedagem Profissional
Clique aqui e ganhe US$ 10,00 para testar durante 1 mês a melhor hospedagem: Digital Ocean!

quarta-feira, 22 de outubro de 2008

Autoboxing: facilidades e performance

Descubra como funciona autoboxing e cuidados com performance que você deve ter

The Java Specialists' Newsletter [Issue 090] - Autoboxing Yourself in JDK 1.5 2004-06-22 

Autor: Dr. Heinz M. Kabutz 

Em meu último curso de Java em Pretoria ( África do Sul ), demonstrei aos alunos como funcionava o recurso de autoboxing. Nossos testes mostraram que autoboxing pode ser ineficiente. Por mais legal que o recurso seja, ele é também especialmente perigoso. Estudantes de Java podem usar autoboxing por engano, afetando a performance de forma negativa. 

Contudo, antes que eu me aprofunde no assunto, eu gostaria de lhes mostrar um pouco sobre o novo loopfor

Por volta de dois anos e meio atrás, reclamei amargamente da síntaxe do Iterator. Eu sentia que usando um loop for ou while juntamente com conversões de tipo tendia a tornar o código-fonte feio. A nova forma de utilização do for no JDK 1.5 finalmente confirma a minha reclamação. Eis como você pode usá-lo em seu código: 

01 import java.util.*;
02 
03 public class NewFor {
04     public static void main(String[] args) {
05         // we can use type-safe collections...
06         Collection names = new ArrayList();
07         names.add("Maxi");
08         names.add("Connie");
09         names.add("Helene");
10         names.add("Heinz");
11 
12         // names.add(new Integer(42)); -- does not compile!
13         // look at the new for construct:
14         for (String name : names) {
15             System.out.println("name = " + name);
16         }
17     }
18 }


Podemos combinar autoboxing com Generics. Autoboxing é o processo de converter tipos primitivos para objetos e vice-versa, automaticamente. Meu código de exemplo na newsletter 40 poderia ser escrito mais elegantemente como: 

1 public void showAging(Collection ages) {
2     for(int age : ages) {
3         System.out.println("Now you're " + age +
4         ", in 3 years time, you'll be " (age + 3));
5     }
6 }


Descobri por acidente que é possível também usar o novo loop for com arrays: 

1 public class NewForArrays {
2     public static void main(String[] args) {
3         String[] names = {"Maxi""Connie""Helene""Heinz"};
4         for (String name : names) {
5             System.out.println("name = " + name);
6         }
7     }
8 }


Não é maravilhoso? Após muito tempo, uma forma consistente de iterar por coleções e arrays. Isso funciona mesmo para arrays de tipos primitivos: 

01 public class NewForPrimitiveArrays {
02     public static void main(String[] args) {
03         int[] daysPerMonth = {31,28,31,30,31,30,31,31,30,31,30,31};
04         int totalDays = 0;
05         
06         for (int days : daysPerMonth) {
07             totalDays += days;
08         }
09         
10         System.out.println("totalDays = " + totalDays);
11     }
12 }


Quando descompilamos a classe, vemos o seguinte ( não tão mal ): 

01 public class NewForPrimitiveArrays {
02     public static void main(String args[]) {
03         int daysPerMonth[] {
04             312831303130313130313031
05         };
06 
07         int totalDays = 0;
08         int arr[] = daysPerMonth;
09         int len = arr.length;
10         
11         for(int i = 0; i < len; i++) {
12             int days = arr[i];
13             totalDays += days;
14         }
15         
16         System.out.println("totalDays = " + totalDays);
17     }
18 }

Um grande medo dos programadores é que você pode usar algumas formas de construção que por sua vez tornam o código inaceitavelmente lento. Eis aqui um código que compara a performance entre a forma "antiga" e a "nova": 

01 import java.util.*;
02 
03 public class NewForPerformance {
04     public static void main(String[] args) {
05         // let's look at the performance difference of for construct
06         Collection numbers = new ArrayList(10000);
07         for(int i=0;i<10000; i++) {
08             // I can add an "int" to a collection of Integer, thanks
09             // to the autoboxing construct shamelessly copied from C#
10             numbers.add((int)Math.random());
11         }
12 
13         oldFor(numbers);
14         newFor(numbers);
15     }
16 
17     private static void oldFor(final Collection numbers) {
18         measureIterations("oldFor"new Runnable() {
19             public void run() {
20                 for(Iterator it = numbers.iterator(); it.hasNext();) {
21                     Integer i = it.next();
22                 }
23             }
24         });
25     }
26 
27     private static void newFor(final Collection numbers) {
28         measureIterations("newFor"new Runnable() {
29             public void run() {
30                 for(Integer i : numbers);
31             }
32         });
33     }
34 
35     private static void measureIterations(String method, Runnable r) {
36         long start = System.currentTimeMillis();
37         int iterations = 0;
38         
39         while(System.currentTimeMillis() - start <= 2000) {
40             r.run();
41             iterations++;
42         }
43 
44         System.out.println(method + ": " + iterations + " in " +
45             (System.currentTimeMillis()-start"ms");
46     }
47 }


Quando rodo este programa, tenho a seguinte saída: 

oldFor: 3532 in 2003ms 
newFor: 3561 in 2003ms 


Ambos métodos são similares o suficiente que podemos declarar que não existe diferença entre eles. Então, você deixaria de usar o novo loop for para ficar lutando com Iterators? 

Perigo espreitando logo abaixo 
Vamos assumir que exércitos de programadores Java irão passar a usar Generics e o novo for. Isso irá toranr arrays redundantes, uma vez que autoboxing nos permite usar ints com Collections ( repare que estamos adicionando e pegando valures da Collection como um tipo de dado primitivo int, mas o tipo de objeto na collection é um Integer ): 

01 import java.util.*;
02 
03 public class AutoBoxing {
04     public static void main(String[] args) {
05         Collection values = new ArrayList();
06         for (int i=0; i<100; i++) {
07             values.add(i);
08         }
09         
10         for(int val : values) {
11             System.out.println(val);
12         }
13     }
14 }


Vamos ver o que acontece quando temos uma collection de números, e desejamos incrementar todos os valores: 

01 import java.util.*;
02 
03 public class AutoBoxingIncrement {
04     public static void main(String[] args) {
05         // Configuramos uma Collection contendo Integers and int[]
06         List values = new ArrayList();
07         int[] valuesArray = new int[1000];
08     
09         for (int i = 0; i < 1000; i++) {
10             values.add(i);
11             valuesArray[i= i;
12         }
13 
14         // Vamos verificar o tempo necessário para incrementar 
15         // os 1000 valores
16         long time = System.currentTimeMillis();
17 
18         // Precisamos fazer isso algumas vezes para vez a diferença
19         for (int j = 0; j < 100000; j++) {
20             for (int i = 0; i < values.size(); i++) {
21                 values.set(i, values.get(i1);
22             }
23         }
24 
25         System.out.println("autoboxing com Generics levou " +
26             (System.currentTimeMillis() - time"ms");
27 
28         // Agora vamos testar com um array
29         time = System.currentTimeMillis();
30         for (int j = 0; j < 100000; j++) {
31             for (int i = 0; i < valuesArray.length; i++) {
32                 valuesArray[i]++;
33             }
34         }
35 
36         System.out.println("Usando um array normal levou " +
37             (System.currentTimeMillis() - time"ms");
38     }
39 }


Quando rodo este programe em meu notebook, vejo uma enorme diferença de performance. O acesso direto ao array de ints é por volta de 20 vezes mais rápida. 

autoboxing com Generics levou 9954ms 
Usando um array normal levou 551ms 

Generics são extremamente fáceis de aprender, e após usando-os por algumas horas, eu não quero voltar para Collections sem tipagem. Contudo, temos que estar conscientes quando fazemos algumas coisas estúpdas que podem impactar na performance, como usar autoboxing quando não devemos. 

Este artigo é fruto de uma parceria entre o GUJ e o autor da The Java Specialists Newsletter, Dr. Heinz M. Kabutz. Originalmente publicadas em inglês e enviadas para milhares de leitores ao redor do planeta, o GUJ propô-se a realizar a tradução para o Português do conteúdo, disponibilizando assim um ótimo material para um número ainda maior de leitores. 

Você encontra o documento original, assim como newsletters anteriores, no site oficial, emhttp://www.javaspecialists.co.za.

Por Rafael Steil 
Fonte : http://www.guj.com.br/java.tutorial.artigo.146.1.guj