Performance de ArrayList e outras Collections

Olá pessoal, recentemente fiz um mini-aplicativo muito interessante que realiza N loops de escrita (utilizando geralmente o método add() ou put()) de objetos utilizando quase todas as variações polimórficas de instânciar uma mesma classe da API Collection do Java, medindo assim o tempo em Millisegundos dos loops de execução de cada tipo de estrutura List, ArrayList, Set, HashSet, Map, HashMap e etc.

Por exemplo abaixo segue alguns dos testes que realizo:

// Testando Instância pura de um ArrayList
private void testingArrayList(int max) {

		ArrayList<Object> array = new ArrayList<Object>();
		long start = System.currentTimeMillis();
		for (int i = 0; i < max; i++) {
			array.add(i);
		}
		long end = System.currentTimeMillis();
		long time = (end - start);
		System.out.println("Tempo: "+time);
	}

// Testando Instância polimórfica de um ArrayList com List

private void testingList2ArrayList(int max) {

		List<Object> array = new ArrayList<Object>();
		long start = System.currentTimeMillis();
		for (int i = 0; i < max; i++) {
			array.add(i);
		}
		long end = System.currentTimeMillis();
		long time = (end - start);
		System.out.println("Tempo: "+time);
	}

// Testando Instância polimórfica de um ArrayList com uma Collection

private void testingCollection2ArrayList(int max) {

		Collection<Object> array = new ArrayList<Object>();
		long start = System.currentTimeMillis();
		for (int i = 0; i < max; i++) {
			array.add(i);
		}
		long end = System.currentTimeMillis();
		long time = (end - start);
		System.out.println("Tempo: "+time);
	}

Resultado de alguns dos testes:

ArrayList -> ArrayList | 5 milliseconds.
List -> ArrayList | 6 milliseconds.
Collection -> ArrayList | 7 milliseconds.

Em muito desses testes a diferença de tempo é perceptível, por mais que esses tempos NÃO signifiquem muita diferença em questão de performance.

É óbvio que exista diferença de tempo entre dois ou mais tipos de estrutura distintos, exemplo ArrayList, LinkedList, Set, Maps, pois cada um possuem suas características peculiares

Porém a minha dúvida é se alguém sabe me explicar, porque dessas diferenças em casos de utilização de uma mesma estrutura que é o caso citado acima, pois está utilizando apenas a estrutura ArrayList.

O código fonte esta nesse link para conferir os inúmeros testes de benchmark que fiz:

espero que gostem e se alguém descobrir me avisem!

caio.ribeiro.pereira

Estou com uma duvida,
em qual versão Java você realizou os teste?

Há mudanças na Java 6 para 7?

Cara aqui tem um link bem interessante sobre isso:

http://blog.caelum.com.br/performance-hashset-em-vez-de-arraylist/

Utilizei Java 6, talvez eu tenha me expressado mal, mas creio que se utilizar o mini-aplicativo e ver meu código-fonte você entenderá melhor a minha dúvida.

Acho (acho) que pelo fato da variável de referência definir quais são os métodos que poderão ser invocados, a JVM custa mais a descobrir quais são esses métodos quando usado uma variável de referência no topo da arvore.

Realmente não faz sentido pra mim, pois se for pensar dessa maneira que eu falei, deveria ser ao contrário. Quando mais em baixo da arvore fosse a referencia, mais métodos poderiam ser chamados, e construtores acionados.

edit:
Desculpe, sobre o primeiro post, eu entendi a pergunta chave errado, mas estou pesquisando. Caso descobrir eu aviso.

Então caio,

acho que seja na parte de organização de cada tipo de vetor que o metodo usa, se ele inseri mais rapido, se ele retira mais rapido, entende?

Seria mais uma organização de dados mais estruturada que a outra(ou menos).

chimufox, é mais ou menos isso tbm que eu creio que a JVM deve trabalhar para gerar essa mínima diferença de tempo

[quote=chimufox]Acho (acho) que pelo fato da variável de referência definir quais são os métodos que poderão ser invocados, a JVM custa mais a descobrir quais são esses métodos quando usado uma variável de referência no topo da arvore.

Realmente não faz sentido pra mim, pois se for pensar dessa maneira que eu falei, deveria ser ao contrário. Quando mais em baixo da arvore fosse a referencia, mais métodos poderiam ser chamados, e construtores acionados.[/quote]

Chimufox

Acho que não, a JVM trata todos metodos igualmente, o que diferencia é a construção do metodo, o que ele faz com as informações.
Como disse, um metodo para organizar um Array, há muitas diretrizes, como em Estrutura de dados, Lista, Filha, Pila.

O benchmark, tal como foi escrito, não é apropriado para determinar as diferenças de tempo porque não deu oportunidade para a JVM efetuar a compilação just-in-time de seu código. Provavelmente você mediu o tempo de execução interpretado, não compilado, porque você:
a) Não efetuou um “aquecimento” dos métodos para que eles fossem compilados antes,
b) Usou muito poucas iterações, e
c) Usou um método pouco preciso para testar (System.currentTimeMillis()).

Segue um link de uma analise feita em Java Collections

Algoritmos

entanglement

a) Não efetuou um “aquecimento” dos métodos para que eles fossem compilados antes.

O que seria fazer um "aquecimento dos métodos"? 

b) Usou muito poucas iterações,

O uso foi de iterações utilizando apenas escrita de objetos, mas futuramente irei detalhar mais os benchmarks, se possível suas sugestões serão bem-vindas!

c) Usou um método pouco preciso para testar (System.currentTimeMillis()).

 Sobre a medição de tempo a princípio eu estava medindo em nanosegundos (System.nanoTime()) , ai mudei para millisegundos pois achei q por mais que as diferenças fossem mais detalhadas, não seria muito útil para benchmark em nanosegundos, é muito detalhe desnecessário, pelo menos na minha opinião.

mateuscs muito interessante essee pdf :smiley:

[quote=entanglement]O benchmark, tal como foi escrito, não é apropriado para determinar as diferenças de tempo porque não deu oportunidade para a JVM efetuar a compilação just-in-time de seu código. Provavelmente você mediu o tempo de execução interpretado, não compilado, porque você:
a) Não efetuou um “aquecimento” dos métodos para que eles fossem compilados antes,
b) Usou muito poucas iterações, e
c) Usou um método pouco preciso para testar (System.currentTimeMillis()).
[/quote]

Essa parece a resposta mais plausível xD

Então o kara usa um teoria de analise interessante para avaliar.

O mais bacana é que usa tipo diversos de dados.

[quote=chimufox][quote=entanglement]O benchmark, tal como foi escrito, não é apropriado para determinar as diferenças de tempo porque não deu oportunidade para a JVM efetuar a compilação just-in-time de seu código. Provavelmente você mediu o tempo de execução interpretado, não compilado, porque você:
a) Não efetuou um “aquecimento” dos métodos para que eles fossem compilados antes,
b) Usou muito poucas iterações, e
c) Usou um método pouco preciso para testar (System.currentTimeMillis()).
[/quote]

Essa parece a resposta mais plausível xD[/quote]

A analise de dados por essa lado, deixou a desejar realmente.

É isso que estamos discutindo a analise feita por ele.

Mais em tese, qual seria melhor chimufox?

Poste opiniões e dados pra gente ver sua resolução
;D

É isso ai pessoal, postem opiniões que ai quando estiver com tempo livre irei melhorar esse mini-app :smiley:

Caio, como na analise feita pelo autor da analise Kapil Viren Ahuja,

Tente usar diversos tipos de dados, desde Integer a Objetos complexos.

E poste a analise ae pra gente
;D

Blz, mas eu ainda gostaria de saber o que significa “aquecimento de métodos”.

Muito interessante este assunto! Pela minha lógica eu diria que ArrayList seria o mais eficiente…

Por exemplo, quando queremos uma lista mais genérica, fazemos:

List lista = new ArrayList();
ou
Collection lista = new ArrayList();

então, na verdade, quando precisamos de algo mais específico e prático, usamos o ArrayList em si, sem os recursos que as outras coleções oferecem, ou seja, uma classe mais enxuta de recursos, o que me leva a crer que tem uma performance melhor (lembrando de que Collection é interface e List uma ‘sub’ interface de Collection)

antes que me joguem pedra e comprovem que eu não entendo nada de Java…só queria dizer que é a minha opinião.

lucasportela é essa a idéia do post, mostrar q faz pequena diferença de performance a maneira como instanciamos uma mesma estrutura só que polimórficamente, se na maioria dos casos só irá utilizar os metodos basicos, pelos testes que fiz no meu mini-benchmark, Instancias usando Collections ou AbstractCollections são as que menos demoram no processamento, mas vale lembrar que ainda tem muitas outras medidas a implementar nesse app para comprovar realmente essa teoria.

enfim, o código-fonte ta ai quem quiser colaborar nele, será bem-vindo!