Java Specialists' Java Training Europehome of the java specialists' newsletter

The Java Specialists' Newsletter
Issue 0902004-06-22 Category: Performance Java version: Sun JDK 1.5.0-beta2

Subscribe Free RSS Feed

Autoboxing: facilidades e performance

by Dr. Heinz M. Kabutz
Special Thank You! I would like to thank Rafael Steil from the Grupo de Usuarios Java - GUJ, from Brazil for translating our newsletters into Portuguese. In addition, I would like to thank Vanessa Sabino for assisting Rafael in the translation work. Disclaimer: Since I do not know any Portuguese, I make no warranty for the accuracy of the translation. Heinz

Autoboxing: facilidades e performance

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 construtor for

Por volta de dois anos e meio atrás, na newsletter TJSN 040, 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:

import java.util.*;

public class NewFor {
  public static void main(String[] args) {
    // we can use type-safe collections...
    Collection<String> names = new ArrayList<String>();
    names.add("Maxi");
    names.add("Connie");
    names.add("Helene");
    names.add("Heinz");
    //names.add(new Integer(42)); -- does not compile!
    // look at the new for construct:
    for (String name : names) {
      System.out.println("name = " + name);
    }
  }
}
  

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:

  public void showAging(Collection<Integer> ages) {
    for(int age : ages) {
      System.out.println("Now you're " + age +
        ", in 3 years time, you'll be " + (age + 3));
    }
  }
  

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

public class NewForArrays {
  public static void main(String[] args) {
    String[] names = {"Maxi", "Connie", "Helene", "Heinz"};
    for (String name : names) {
      System.out.println("name = " + name);
    }
  }
}
  

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:

public class NewForPrimitiveArrays {
  public static void main(String[] args) {
    int[] daysPerMonth = {31,28,31,30,31,30,31,31,30,31,30,31};
    int totalDays = 0;
    for (int days : daysPerMonth) {
      totalDays += days;
    }
    System.out.println("totalDays = " + totalDays);
  }
}
  

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

public class NewForPrimitiveArrays {
  public static void main(String args[]) {
    int daysPerMonth[] = {
      31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
    };
    int totalDays = 0;
    int arr$[] = daysPerMonth;
    int len$ = arr$.length;
    for(int i$ = 0; i$ < len$; i$++) {
      int days = arr$[i$];
      totalDays += days;
    }
    System.out.println("totalDays = " + totalDays);
  }
}

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":

import java.util.*;

public class NewForPerformance {
  public static void main(String[] args) {
    // let's look at the performance difference of for construct
    Collection<Integer> numbers = new ArrayList<Integer>(10000);
    for(int i=0;i<10000; i++) {
      // I can add an "int" to a collection of Integer, thanks
      // to the autoboxing construct shamelessly copied from C#
      numbers.add((int)Math.random());
    }
    oldFor(numbers);
    newFor(numbers);
  }
  private static void oldFor(final Collection<Integer> numbers) {
    measureIterations("oldFor", new Runnable() {
      public void run() {
        for(Iterator<Integer> it = numbers.iterator(); it.hasNext();) {
          Integer i = it.next();
        }
      }
    });
  }
  private static void newFor(final Collection<Integer> numbers) {
    measureIterations("newFor", new Runnable() {
      public void run() {
        for(Integer i : numbers);
      }
    });
  }
  private static void measureIterations(String method, Runnable r) {
    long start = System.currentTimeMillis();
    int iterations = 0;
    while(System.currentTimeMillis() - start <= 2000) {
      r.run();
      iterations++;
    }
    System.out.println(method + ": " + iterations + " in " +
        (System.currentTimeMillis()-start) + "ms");
  }
}

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. sso irá tornar 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):

import java.util.*;

public class AutoBoxing {
  public static void main(String[] args) {
    Collection<Integer> values = new ArrayList<Integer>();
    for (int i=0; i<100; i++) {
      values.add(i);
    }
    for(int val : values) {
      System.out.println(val);
    }
  }
}
  

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

import java.util.*;

public class AutoBoxingIncrement {
  public static void main(String[] args) {
    // we set up a Collection containing Integers and an int[]
    List<Integer> values = new ArrayList<Integer>();
    int[] valuesArray = new int[1000];
    for (int i = 0; i < 1000; i++) {
      values.add(i);
      valuesArray[i] = i;
    }

    // let's time how quickly we can increment the 1000 values
    long time = System.currentTimeMillis();
    // we must do it a few times to see the difference
    for (int j = 0; j < 100000; j++) {
      for (int i = 0; i < values.size(); i++) {
        values.set(i, values.get(i) + 1);
      }
    }
    System.out.println("autoboxing with generics took " +
        (System.currentTimeMillis() - time) + "ms");

    // now we try with an array
    time = System.currentTimeMillis();
    for (int j = 0; j < 100000; j++) {
      for (int i = 0; i < valuesArray.length; i++) {
        valuesArray[i]++;
      }
    }
    System.out.println("Using a plain array took " +
        (System.currentTimeMillis() - time) + "ms");
  }
}
  

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.

Saudações,

Heinz

Performance Articles Related Java Course Discuss at The Java Specialist Club

Language
Performance
What is the Java Specialists Club?

© 2010 Heinz Kabutz - All Rights Reserved Sitemap seo web design Catch22 Marketing
Oracle and Java are registered trademarks of Oracle and/or its affiliates. Other names may be trademarks of their respective owners. JavaSpecialists.eu is not connected to Oracle, Inc. and is not sponsored by Oracle, Inc.