JavaTips 3.0 – Performance Generics + AutoBox/UnBox
Março 19, 2008
AutoBox/UnBox é a conversão de forma automática entre objetos e tipos primitivos, dicas:
- Use somente quando houver “descasamento” entre tipos primitivos e objetos empacotadores
- Não abuse: um Integer não substitui um int (a performance é pior)
- Cuidado, unboxing com objetos null lança exceção
Probolema:
- Generics não trabalha com tipos primitivos
Após executar o seguinte teste de performance, constatou-se uma diferênça em média de 25% com uso do autobox. O algoritimo de teste baseou-se em comparar as duas execuções:
- faça 1000 vezes
- varra um ArrayList<Integer>, capturando o valor sem fazer autobox
- faça 1000 vezes
- varra um ArrayList<Integer> usando enhanced-loop fazendo automaticamente unbox para int, capturando o valor e fazendo autbox novamente para um objeto Integer.
Resultado da execução:
___________________________________
autoBoxUnBox()…java.lang.NullPointerException
Sem autoboxing = 6531ms
Com autobox/unbox= 8734ms
Diferença de 26%
___________________________________
Conclusão:
Se sua aplicação é “Performance Driven”, estruturas como o IntHashMap visto no blog anterior entre outras coleções da API que não trabalhem com Generics podem ser a melhor saída para escovar milisegundos de processamento.
Abaixo segue a classe usada no teste:
package update5.boxing;
import java.util.*;
/**
*
* @author Fabrício Silva Epaminondas
*/
public class AutoboxingUnboxing {
static void autoBoxUnBox() {
// Antes
int prim = 10;// primitivo
Integer obj = new Integer(20);// objeto
Integer nullObj = null;
// Java 5
Integer c = 30;// autobox Integer c = new Integer(30)
prim = obj;// unbox prim = obj.intValue();
obj = prim;// autobox obj = new Integer(prim);
try{
prim = nullObj;// throw NullPointerException
}catch (NullPointerException e) {
System.out.println("autoBoxUnBox()..." + e);
}
}
@SuppressWarnings("unchecked")
static void autoboxComGenerics() {
// Antes
Map products = new HashMap();
products.put(new Integer(2779), new Float(20.45f));
products.put(Integer.valueOf(922), Float.valueOf(99.99f));
float price = new Float((Float) products.get(new Integer(922)
.intValue())).floatValue();
// AutoBoxing com Generics
Map<Integer, Float> products2 = new HashMap<Integer, Float>();
products2.put(10, 20.45f);
products2.put(20, 99.99f);
float price2 = products2.get(10);
// Problema: Performance
// Generics só aceita Tipos de Objetos e não primitivos
// Ex: ArrayList de Integers..
Collection<Integer> values = new ArrayList<Integer>();
for (int i = 0; i < 100000; i++) {
values.add(i);
}
Integer intObj = null;
// Teste de Performance 1 (Sem Autobox)
long time = System.currentTimeMillis();
// repete 1000 vezes
for (int i = 0; i < 1000; i++) {
// for em coleção de objetos Integer
for (Integer val : values) {
intObj = val;// sem autobox
}
}
long performance1 = System.currentTimeMillis() - time;
System.out.println("Sem autoboxing = " + performance1 + "ms");
// Teste de Performance 2 (Com Autobox/Unbox)
time = System.currentTimeMillis();
// repete 1000 vezes
for (int i = 0; i < 1000; i++) {
// for em coleção de objetos Integer
for (int val : values) {// executando unboxing para int
intObj = val;// autobox para Integer
}
}
long performance2 = System.currentTimeMillis() - time;
System.out.println("Com autobox/unbox= " + performance2 + "ms");
System.out.println("Diferença de " + percentGain(performance1,performance2) + "%");
}
static long percentGain(long num, long div) {
return (100 - num*100/div);
}
public static void main(String[] args) {
autoBoxUnBox();
autoboxComGenerics();
}
}