|
|
Rafael Steil
Descubra como funciona autoboxing e cuidados com performance que você deve ter
Autoboxing: facilidades e performance
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 loop for.
O Novo loop for
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<String> names = new ArrayList<String>();
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<Iterator> 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 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
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<Integer> numbers = new ArrayList<Integer>(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<Integer> numbers) {
18 measureIterations("oldFor", new Runnable() {
19 public void run() {
20 for(Iterator<Integer> it = numbers.iterator(); it.hasNext();) {
21 Integer i = it.next();
22 }
23 }
24 });
25 }
26
27 private static void newFor(final Collection<Integer> 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<Integer> values = new ArrayList<Integer>();
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<Integer> values = new ArrayList<Integer>();
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(i) + 1);
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.
Sobre a The Java Specialists Newsletter
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, em http://www.javaspecialists.co.za.
Copyright 2000-2003 Maximum Solutions, South Africa
Reprint Rights. Copyright subsists in all the material included in this email, but you may freely share the entire newsletter with anyone you feel may be interested, and you may reprint excerpts both online and offline provided that you acknowledge the source as follows: This material from The Java(tm) Specialists' Newsletter by Maximum Solutions (South Africa). Please contact Maximum Solutions for more information.
Java and Sun are trademarks or registered trademarks of Sun Microsystems, Inc. in the United States and other countries. Maximum Solutions is independent of Sun Microsystems, Inc.
|
|
|