Em um papo de almoço, conversávamos sobre instanciar ou não instanciar uma coleção membro de alguma classe do domínio, exemplo:
public class Cliente {
List enderecos; //null por default
}
ou
public class Cliente {
List enderecos = new ArrayList();
}
E a questão fica entre performance e complexidade.
Performance
Performance por que quando um Cliente é instanciado, o seu atributo, enderecos, não é instanciado junto. No segundo caso, cada vez que em algum lugar em der um new Cliente() automaticamente está acontecendo um new ArrayList() , que constrói um Array interno de 10 posições (referências nulas claro).
Complexidade
Imagine que a classe Cliente seja um JavaBean, e a partir de uma classe cliente eu deseje iterar os endereços:
public class MinhaClasseDeNegocio {
public Endereco enderecoDefault(Integer idCliente) {
Cliente cliente = meuDataSource.find(idCliente);
for (Endereco e : cliente.getEnderecos()) {
if (e.isDefault()) return e;
}
return null;
}
}
O que acontecerá se o Cliente não tiver endereços? Tchananam... NullPointerException!
Então, se há a possibilidade de a lista vir vazia, eu tenho que lidar com isso, garantir, como abaixo:
public class MinhaClasseDeNegocio {
public Endereco enderecoDefault(Integer idCliente) {
Cliente cliente = meuDataSource.find(idCliente);
if (cliente.getEndereco() != null) {
for (Endereco e : cliente.getEnderecos()) {
if (e.isDefault()) return e;
}
}
return null;
}
}
Obs.: Existe o modo Domain-Driven (o qual prefiro) de lidar com isso, mas fica para outro tópico.
Profiling
Para ilustrar a performance, fiz um pequeno benchmark, que pode ser visto abaixo:
import java.util.*;
public class ArrayProfile {
static final int ITENS = 1000000;
public static void main (String[] args) throws Exception {
long memoryBefore = Runtime.getRuntime().totalMemory();
long startTime = System.currentTimeMillis();
List lista = new ArrayList();
for (int i = 0; i < ITENS; i++) {
lista.add(new FaixaRepository());
}
long endTime = System.currentTimeMillis();
Thread.sleep(1000);
long memoryAfter = Runtime.getRuntime().totalMemory();
System.out.format("%nItens instanciados: %,d %n", ITENS);
System.out.format("%nMemória usada: %,d bytes %n", (memoryAfter - memoryBefore));
System.out.format("%nTempo necessário: %d ms %n%n", (endTime - startTime));
}
}
class FaixaRepository {
List lista = new ArrayList();
}
class Faixa {
Integer codigo;
Double remuneracao;
}
Neste primeiro caso, eu instancio o ArrayList, membro de FaixaRepository, e o resultado foi o seguinte:
mtorres@independiente:~/temp/sparring/collections$ java ArrayProfile
Itens instanciados: 1.000.000
Memória usada: 101.974.016 bytes
Tempo necessário: 1359 ms
Fazendo uma alteração, deixando o membro lista nulo, como abaixo:
...
class FaixaRepository {
List lista;
}
...
Obtive o seguinte resultado:
mtorres@independiente:~/temp/sparring/collections$ java ArrayProfile
Itens instanciados: 1.000.000
Memória usada: 9.437.184 bytes
Tempo necessário: 111 ms
Conclusão
Este é outro caso em que não se está definido o que é certo ou errado, cada caso é um caso, faz parte do design decidir qual estratégia será utilizada.
Obs: O tempo que eu uso para fazer estes micro artigos é a espera do build e do start dos servidores, eu instrumentei, hehehe, abaixo:
mtorres@independiente:~/desenvolvimento/bla/bla/bla$ time mvn install -Plocal
======= Construindo ... bla bla bla =======
... muitas linhas depois ....
real 0m48.702s
user 0m35.430s
sys 0m2.904s
mtorres@independiente:~/desenvolvimento/bla/bla/bla$ $ time mvn jetty:run -Plocal
... bla bla bla ...
real 0m28.685s
user 0m29.782s
sys 0m0.844s
mtorres@independiente:~/desenvolvimento/bla/bla/bla$ $ time mvn jetty:run -Plocal
... bla bla bla ...
real 0m12.671s
user 0m12.369s
sys 0m0.300s
Nenhum comentário:
Postar um comentário