Qual é a biblioteca Java Collections mais eficiente? [fechadas]

135

Qual é a biblioteca Java Collections mais eficiente?

Alguns anos atrás, eu fiz muito Java e tive a impressão de que o trove é a melhor (mais eficiente) implementação de Coleções Java. Mas quando li as respostas para a pergunta " Bibliotecas Java gratuitas mais úteis? ", Notei que o tesouro quase não é mencionado. Então, qual biblioteca Java Collections é melhor agora?

ATUALIZAÇÃO: Para esclarecer, quero saber principalmente qual biblioteca usar quando tiver que armazenar milhões de entradas em uma tabela de hash etc. (preciso de um pequeno tempo de execução e presença de memória).

Frank
fonte
Quais são as chaves e os valores nesta tabela? Se eles não são primitivos, o que há de errado com o HashMap normal, etc?
Jon Skeet
Para um mapa muito grande, você pode querer uma implementação de sondagem, ou mesmo alinhada como uma tabela de banco de dados.
Tom Hawtin - tackline 10/03/09
1
Curiosamente, não vejo menção a Colt aqui, que foi posteriormente incluída em Mahout.
smartnut007
4
Vale mencionar uma biblioteca de coleções muito boa - coleções GS (github.com/goldmansachs/gs-collections). Tem documentação excelente e um conjunto exaustivo de colecções mutáveis e imutáveis
Piotr Kochanski

Respostas:

73

Da inspeção, parece que o Trove é apenas uma biblioteca de coleções para tipos primitivos - não é como se quisesse adicionar muita funcionalidade às coleções normais no JDK.

Pessoalmente (e sou tendencioso), adoro o Guava (incluindo o projeto anterior do Google Java Collections). Isso facilita várias tarefas (incluindo coleções), de uma maneira que seja pelo menos razoavelmente eficiente. Dado que as operações de coleção raramente formam um gargalo no meu código (na minha experiência), isso é "melhor" do que uma API de coleções que pode ser mais eficiente, mas não torna meu código tão legível.

Dado que a sobreposição entre o Trove e a goiaba é praticamente nula, talvez você possa esclarecer o que realmente está procurando em uma biblioteca de coleções.

Jon Skeet
fonte
3
@ Andréas: Não posso dizer que concordo. Não que seja um cenário "um ou outro" - eu uso as coleções regulares (com ajudantes como a classe Lists) e depois uso Iterables etc. quando preciso. Use a complexidade apenas quando isso o ajudar.
Jon Skeet
10
depois de ler meu próprio comentário vários meses depois de usar extensivamente o GC - discordo de minha opinião passada e concordo plenamente com a sua. use os métodos / classes auxiliares extensivamente, eles tornam muito mais legível e seguro.
Andreas Petersson
1
@Andreas: Obrigado por voltar e dizer isso - Fico feliz em ouvir que GJC está ajudando :)
Jon Skeet
2
Ei, Jon, o Google Java Collections agora é Guava . Você pode querer atualizar seu post para futuras referências :)
Artur Czajka
1
Trabalhei em alguns projetos intensivos em dados, em que as coleções eram um grande gargalo. As coleções Java são terrivelmente ineficientes (memória e velocidade), especialmente se armazenarem primitivas.
Jay Askren
104

A questão é (agora) sobre o armazenamento de muitos dados, que podem ser representados usando tipos primitivos como int, em um mapa. Algumas das respostas aqui são muito enganosas na minha opinião. Vamos ver o porquê.

Modifiquei o benchmark do trove para medir o tempo de execução e o consumo de memória. Também adicionei o PCJ a esse benchmark, que é outra biblioteca de coleções para tipos primitivos (eu uso esse extensivamente). O benchmark 'oficial' do tesouro não compara o IntIntMaps ao Java Collection Map<Integer, Integer>, provavelmente armazenar Integerse armazenar intsnão é o mesmo do ponto de vista técnico. Mas um usuário pode não se importar com esses detalhes técnicos, ele deseja armazenar dados representáveis ​​com intseficiência.

Primeiro, a parte relevante do código:

new Operation() {

     private long usedMem() {
        System.gc();
        return Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();
     }

     // trove
     public void ours() {
        long mem = usedMem();
        TIntIntHashMap ours = new TIntIntHashMap(SET_SIZE);
        for ( int i = dataset.size(); i-- > 0; ) {
           ours.put(i, i);
        }
        mem = usedMem() - mem;
        System.err.println("trove " + mem + " bytes");
        ours.clear();
     }

     public void pcj() {
        long mem = usedMem();
        IntKeyIntMap map = new IntKeyIntOpenHashMap(SET_SIZE);
        for ( int i = dataset.size(); i-- > 0; ) {
           map.put(i, i);
        }
        mem = usedMem() - mem;
        System.err.println("pcj " + mem + " bytes");
        map.clear();
     }

     // java collections
     public void theirs() {
        long mem = usedMem();
        Map<Integer, Integer> map = new HashMap<Integer, Integer>(SET_SIZE);
        for ( int i = dataset.size(); i-- > 0; ) {
           map.put(i, i);
        }
        mem = usedMem() - mem;
        System.err.println("java " + mem + " bytes");
        map.clear();
     }

Presumo que os dados sejam primitivos ints, o que parece sensato. Mas isso implica em uma penalidade de tempo de execução para o java util, devido ao boxing automático, que não é necessário para as estruturas de coleções primitivas.

Os resultados do tempo de execução (sem gc()chamadas, é claro) no WinXP, jdk1.6.0_10:

                      100000 operações de venda 100000 contém operações 
coleções java 1938 ms 203 ms
trove 234 ms 125 ms
pcj 516 ms 94 ms

Embora isso possa já parecer drástico, esse não é o motivo para usar essa estrutura.

O motivo é o desempenho da memória. Os resultados para um mapa contendo 100000 intentradas:

coleções java oscilam entre 6644536 e 7168840 bytes
trove 1853296 bytes
pcj 1866112 bytes

O Java Collections precisa de mais de três vezes a memória em comparação com as estruturas de coleta primitivas. Ou seja, você pode manter três vezes mais dados na memória, sem recorrer às E / S de disco, o que diminui o desempenho do tempo de execução por magnitudes. E isso importa. Leia alta escalabilidade para descobrir o porquê.

Na minha experiência, o alto consumo de memória é o maior problema de desempenho do Java, o que obviamente resulta em pior desempenho do tempo de execução. As estruturas de coleta primitivas podem realmente ajudar aqui.

Então: Não, java.util não é a resposta. E "adicionar funcionalidade" às ​​coleções Java não é o ponto de perguntar sobre eficiência. Além disso, as coleções modernas do JDK " não superam nem as coleções especializadas da Trove".

Isenção de responsabilidade: A referência aqui está longe de ser completa, nem perfeita. O objetivo é esclarecer a questão, que experimentei em muitos projetos. Coleções primitivas são úteis o suficiente para tolerar API duvidosa - se você trabalha com muitos dados.

the.duckman
fonte
3
Na verdade, acho que sua resposta é enganosa. Armazenar ints x inteiros é muito diferente e, provavelmente, o principal motivo do aumento do uso de memória. Concordo que uma estrutura de coleta de tipo bruto pode ser útil, mas não torna o trove ou o pcj "melhor" que o java.util.
Jorn
22
A questão é sobre o armazenamento eficiente de dados int. Não se trata de armazenar números inteiros. Para esta tarefa, o trove / pcj é mais eficiente, como tentei mostrar. O uso de números inteiros impõe ineficiências de tempo de execução e memória. Como o java.util não permite o uso de primitivas, não é a melhor opção para esta tarefa.
the.duckman
2
(para comunidade russa) aqui vai outra referência: total-holywar.blogspot.com/2011/07/...
dma_k
Não temos certeza se não usamos int como chave, apenas String normal. Qual será o resultado da bancada para eles?
Clark Bao
@ClarkBao (desculpe pelo atraso) O armazenamento de qualquer objeto como chave utilizará o objeto hashCode(). Você recebe intcomo a chave.
Matthieu #
47

Eu sei que este é um post antigo e há uma tonelada de respostas aqui. Mas, as respostas acima são superficiais e simplificadas em termos de sugestão de uma biblioteca. Não existe uma biblioteca que se dê bem nos vários benchmarks apresentados aqui. A única conclusão que tirei é que se você se preocupa com desempenho e memória e, especificamente, com tipos primitivos, vale mais a pena olhar para as alternativas não jdk.

Aqui está uma análise mais sólida, em termos de mecânica de referência e das bibliotecas cobertas. Este é um tópico na lista de desenvolvedores mahout.

As bibliotecas cobertas são

  • HPPC
  • Trove
  • FastUtil
  • Mahout (Colt)
  • Coleções Java

Atualização em junho de 2015 : Infelizmente, os benchmarks originais não estão mais disponíveis e, além de um pouco desatualizados. Aqui estão alguns benchmarks recentes (janeiro de 2015) feitos por outra pessoa. Não é tão abrangente nem possui as ferramentas exploratórias interativas como o link original.

smartnut007
fonte
1
Obrigado. Isso foi muito útil. Considerando a importância da pergunta, é difícil acreditar que nenhuma das outras respostas (exceto as do the.duckman) realmente responda a essa pergunta.
Dexter
20

Como outros comentaristas notaram, a definição de "eficiente" lança uma ampla rede. No entanto, ninguém ainda mencionou a biblioteca Javolution .

Alguns dos destaques:

  • As classes Javolution são rápidas, muito rápidas (por exemplo, inserção / exclusão de texto em O [Log (n)] em vez de O [n] para StringBuffer / StringBuilder padrão).
  • Todas as classes Javolution são compatíveis com o tempo real e têm um comportamento altamente determinístico (no intervalo de microssegundos). Além disso (ao contrário da biblioteca padrão), o Javolution é seguro para RTSJ (sem conflito de memória ou vazamento de memória quando usado com a extensão Java Real-Time).
  • As classes de coleção em tempo real do Javolution (mapa, lista, tabela e conjunto) podem ser usadas no lugar da maioria das classes de coleção padrão e fornecem funcionalidade adicional.
  • As coleções Javolution fornecem garantias de simultaneidade para facilitar a implementação de algoritmos paralelos.

A distribuição Javolution inclui um conjunto de benchmarks para que você possa ver como eles se comparam com outras bibliotecas / coleções internas.

sstock
fonte
16

Algumas bibliotecas de coleção a serem consideradas:

Em primeiro lugar, procuraria a biblioteca de coleções do JDK. Ele cobre as coisas mais comuns que você precisa fazer e, obviamente, já está disponível para você.

O Google Collections é provavelmente a melhor biblioteca de alta qualidade fora do JDK. É muito usado e bem suportado.

O Apache Commons Collections é mais antigo e sofre um pouco com o problema "muitos cozinheiros", mas também possui muitas coisas úteis.

O Trove possui coleções muito especializadas para casos como valores / chaves primitivas. Atualmente, descobrimos que em JDKs modernos e com as coleções Java 5+ e casos de uso simultâneos, as coleções JDK superam até as coleções Trove especializadas.

Se você tem casos de uso de simultaneidade realmente alta, deve definitivamente verificar coisas como o NonBlockingHashMap na lib de alta escala, que é uma implementação sem bloqueios e pode pisar no ConcurrentHashMap se você tiver o caso de uso correto.

Alex Miller
fonte
7
"Atualmente, descobrimos que nos JDKs modernos e com as coleções Java 5+ e casos de uso simultâneos, as coleções JDK superam até as coleções especializadas da Trove." Enganador - nunca vi uma micro-referência em que o armazenamento / recuperação de tipos primitivos em uma classe especializada de coleta primitiva como o Trove não superou as classes de coleta JDK no uso da memória e no tempo da CPU. No entanto, se você estiver usando objetos (e não tipos primitivos), eu concordo com Alex, preocupar-se com a coleção impl não é tão importante assim.
Riyad Kalla
2
Essa declaração foi baseada no uso pesado do mundo real (que eu assumirei uma micro-referência todos os dias) de vários itens de coleção onde antes tínhamos precisado de uma coleção Trove, mas agora conseguimos retirá-la. As atualizações tardias do JDK 6 (por volta do final de 2009) realmente forneceram código personalizado para chaves de mapa comuns, como Integer, que melhoraram substancialmente alguns dos usos mais comuns.
Alex Miller #
1
Alex, não duvido nos seus casos de uso específicos que retirar coleções primitivas e seguir coleções JDK foi rápido o suficiente, mas acenou com a mão pela paisagem que é coleções e disse: "Todos que passam, é rápido o suficiente! " não é preciso. Se estou trabalhando em um mecanismo de jogo 2D, a sobrecarga de boxe / unboxing dos meus tipos primitivos constantemente é mensurável. Se eu estiver trabalhando em uma API REST, então não, provavelmente não fará uma diferença mensurável em relação a operações muito mais caras, como a E / S HTTP. Apenas me senti compelido a quantificar sua postagem.
Riyad Kalla
4
Não acho que alguém que esteja lendo isso deva ouvir qualquer um de nós. Eles devem testar seu próprio caso de uso e ver qual é o melhor desempenho. Meus comentários são baseados nos testes de desempenho bastante agressivos da minha equipe com uma variedade de bibliotecas. YMMV.
Alex Miller
2
Eu concordo com @Riyad. Estou escrevendo um conjunto de autômatos finitos de alto desempenho e o implementei com o Trove e o Java Collections Framework (última atualização do jdk 6). Trove supera o grande momento. Na ordem de dezenas de vezes melhor na velocidade de computação e no consumo de memória.
Nico Huysamen
6

java.util

Desculpe a resposta óbvia, mas para a maioria dos usos, as coleções Java padrão são mais que suficientes.

Yuval Adam
fonte
4
Para usos básicos, sim. Mas acho que o framework perde alguns recursos básicos e avançados (como coleções imutáveis, filtros, multimaps, etc.) e é aí que (por exemplo) o Google Collections entra
Jorn
1
Eu acho que esta resposta erra o ponto. O JCF provavelmente foi incrível em 2002, quando as pessoas não usavam o Java por muito tempo. Infelizmente, ele não envelheceu bem, especialmente quando comparado ao suporte a coleções de outros idiomas da JVM.
Ted Pennings
3
-1 A questão é "mais eficiente para armazenar int" e qualquer mencionado exemplo é melhor do que java.util
kommradHomer
6

Para armazenar milhões Stringem um mapa, consulte http://code.google.com/p/flatmap

akuhn
fonte
3
+1 Você pode apresentar como foi aprimorado?
Clark Bao
1
Deve haver postagens de blog do autor do flatmap em algum lugar da Internet.
akuhn
3

O ConcurrentHashMap e o java.util.concurrentpacote devem ser mencionados, se você planeja usar o HashMap em vários threads. uma pegada de memória pequena é avaliada, pois isso faz parte do java padrão.

Andreas Petersson
fonte
3

Depende de como definimos "eficiente".

Toda estrutura de dados tem seu próprio comportamento Big-Oh para leitura, gravação, iteração, presença de memória, etc. É provável que uma lista vinculada em uma biblioteca seja a mesma que em qualquer outra. E um mapa de hash será mais rápido para ler O (1) do que uma lista vinculada O (n).

Mas quando li as respostas para a pergunta "Bibliotecas Java gratuitas mais úteis?" Notei que o tesouro é dificilmente mencionado.

Isso não soa como "mais eficiente". Parece "mais popular" para mim.

Apenas alguns comentários - nunca ouvi falar disso e não conheço ninguém que o tenha usado. As coleções incorporadas ao JDK, Google ou Apache Commons são bem conhecidas para mim.

duffymo
fonte
3

O Trove oferece algumas vantagens.

  • menor espaço de memória, ele não usa objetos Map.Entry
  • você pode usar estratégias de hash em vez de chaves para mapas, economizando memória e significa que você não precisa definir uma nova chave cada vez que deseja armazenar em cache um objeto em um novo conjunto de atributos
  • possui tipos de coleção primitivos
  • acho que tem alguma forma de iterador interno

Dito isto, muito foi feito para melhorar as coleções do jdk desde que o trove foi escrito.

São as estratégias de hash que o tornam atraente para mim ... no Google, para descobrir e ler sua visão geral.

duffymo
fonte
2

Se você deseja armazenar milhões de registros em uma tabela de hash, é provável que você tenha problemas de memória. Isso aconteceu comigo quando tentei criar um mapa com 2,3 milhões de objetos String, por exemplo. Fui com o BerkeleyDB , que é muito maduro e tem um bom desempenho. Eles possuem uma API Java que envolve a API Collections, para que você possa criar facilmente mapas arbitrariamente grandes, com muito pouco espaço para memória. O acesso será mais lento (como é armazenado no disco).

Pergunta de acompanhamento : existe uma biblioteca decente (e eficiente) e bem mantida para coleções imutáveis? O Clojure tem um excelente suporte para isso e seria bom ter algo semelhante para Java.

fred-o
fonte
1
As coleções do Google adicionam coleções imutáveis.
the.duckman