Qual è la libreria di raccolte Java più efficiente? [chiuso]


135

Qual è la libreria di raccolte Java più efficiente?

Alcuni anni fa, ho fatto un sacco di Java e ha avuto l'impressione di allora che trove è la migliore (più efficiente) implementazione Java collezioni. Ma quando ho letto le risposte alla domanda " La maggior parte delle librerie Java utili gratis? " Ho notato che tesoro è appena accennato. Quindi quale libreria di raccolte Java è la migliore ora?

AGGIORNAMENTO: Per chiarire, desidero principalmente sapere quale libreria utilizzare quando devo archiviare milioni di voci in una tabella hash ecc. (Ho bisogno di un piccolo runtime e footprint di memoria).


Quali sono le chiavi e i valori in questa tabella? Se non sono primitivi, cosa c'è di sbagliato nella normale HashMap ecc.?
Jon Skeet,

Per una mappa molto grande potresti voler implementare un sondaggio, o addirittura inline come una tabella di database.
Tom Hawtin: affronta il

1
È interessante notare che non vedo alcuna menzione di Colt qui che è stato successivamente inserito in Mahout.
smartnut007,

4
Vale la pena menzionare una biblioteca di collezioni molto bella: le collezioni GS (github.com/goldmansachs/gs-collections). Ha una documentazione eccellente e un insieme esaustivo di raccolte mutabili e immutabili
Piotr Kochański

Risposte:


73

Dall'ispezione, sembra che Trove sia solo una libreria di raccolte per tipi primitivi - non è come se intendesse aggiungere molta funzionalità rispetto alle normali raccolte in JDK.

Personalmente (e sono di parte) adoro Guava (incluso l'ex progetto Google Java Collections). Rende varie attività (comprese le raccolte) molto più semplici, in un modo almeno ragionevolmente efficiente. Dato che le operazioni di raccolta raramente costituiscono un collo di bottiglia nel mio codice (nella mia esperienza), questo è "migliore" di un'API di raccolta che può essere più efficiente ma non rende il mio codice leggibile.

Dato che la sovrapposizione tra Trove e Guava è praticamente nulla, forse potresti chiarire cosa stai effettivamente cercando da una biblioteca di collezioni.


3
@Andreas: non posso dire che sono d'accordo. Non che sia uno "uno o l'altro" scenario - uso le raccolte regolari (con aiutanti come la classe Liste) e quindi uso Iterables ecc. Quando ne ho bisogno. Usa la complessità solo quando ti aiuta.
Jon Skeet,

10
dopo aver letto il mio commento diversi mesi dopo aver ampiamente utilizzato GC - non sono d'accordo con la mia opinione passata e sono pienamente d'accordo con la tua. usano ampiamente i metodi / classi helper, rendono gran parte del codice più leggibile e più sicuro.
Andreas Petersson,

1
@Andreas: Grazie per essere tornato e averlo detto - Sono felice di sapere che GJC sta aiutando :)
Jon Skeet

2
Ehi, Jon, Google Java Collections ora è Guava . Potresti voler aggiornare il tuo post per riferimenti futuri :)
Artur Czajka,

1
Ho lavorato su alcuni progetti ad alta intensità di dati in cui le raccolte rappresentavano un enorme collo di bottiglia. Le raccolte Java sono terribilmente inefficienti (sia in termini di memoria che di velocità) soprattutto se contengono primitive.
Jay Askren,

104

La domanda è (ora) sulla memorizzazione di molti dati, che possono essere rappresentati usando tipi primitivi come int, in una Mappa. Alcune delle risposte qui sono molto fuorvianti secondo me. Vediamo perché.

Ho modificato il benchmark da trove a misurare sia il tempo di esecuzione che il consumo di memoria. Ho anche aggiunto PCJ a questo benchmark, che è un'altra libreria di raccolte per tipi primitivi (la uso ampiamente). Il benchmark "ufficiale" non confronta IntIntMaps con Java Collection Map<Integer, Integer>, probabilmente l'archiviazione Integerse l'archiviazione intsnon sono le stesse dal punto di vista tecnico. Ma a un utente potrebbe non interessare questo dettaglio tecnico, desidera archiviare i dati rappresentabili in modo intsefficiente.

Innanzitutto la parte pertinente del codice:

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 che i dati siano primitivi ints, il che sembra sano. Ma ciò implica una penalità di runtime per java util, a causa del boxing automatico, che non è necessario per i framework di raccolte primitive.

I risultati di runtime (senza gc()chiamate, ovviamente) su WinXP, jdk1.6.0_10:

                      100000 operazioni put 100000 contiene operazioni 
raccolte java 1938 ms 203 ms
trove 234 ms 125 ms
pcj 516 ms 94 ms

Sebbene ciò possa già sembrare drastico, non è questo il motivo per utilizzare un tale framework.

Il motivo è la prestazione di memoria. I risultati per una mappa contenente 100000 intvoci:

Le raccolte java oscilla tra 6644536 e 7168840 byte
trove 1853296 byte
pcj 1866112 byte

Le raccolte Java richiedono una memoria tre volte superiore rispetto ai framework di raccolta primitivi. Vale a dire che è possibile conservare tre volte più dati in memoria, senza ricorrere all'IO del disco che riduce le prestazioni di runtime per grandezza. E questo conta. Leggi l'alta scalabilità per scoprire perché.

Nella mia esperienza, l'elevato consumo di memoria è il più grande problema di prestazioni con Java, che ovviamente comporta anche peggiori prestazioni di runtime. I framework di raccolta primitivi possono davvero aiutare qui.

Quindi: No, java.util non è la risposta. E "aggiungere funzionalità" alle raccolte Java non è il punto quando si chiede efficienza. Anche le moderne collezioni JDK non "superano nemmeno le collezioni specializzate Trove".

Disclaimer: il benchmark qui è tutt'altro che completo, né è perfetto. È pensato per portare a casa il punto, che ho sperimentato in molti progetti. Le raccolte primitive sono abbastanza utili per tollerare l'API fishy - se lavori con molti dati.


3
In realtà, penso che la tua risposta sia fuorviante. La memorizzazione di ints vs Integer è molto diversa, e molto probabilmente il motivo principale dell'aumento dell'utilizzo della memoria. Concordo sul fatto che un framework di raccolta di tipo raw potrebbe essere utile, ma non rende trove o pcj "migliore" di java.util.
Jorn

22
La domanda riguarda la memorizzazione efficiente dei dati int. Non riguarda la memorizzazione di numeri interi. Per questo compito trove / pcj sono più efficienti, come ho cercato di mostrare. L'uso di numeri interi impone inefficienze di runtime e memoria. Poiché java.util non consente l'utilizzo di primitivi, non è la scelta migliore per questo compito.
the.duckman

2
(per la comunità russa) ecco un altro punto di riferimento: total-holywar.blogspot.com/2011/07/…
dma_k

Non sono sicuro se non usiamo int come chiave, solo normale String. Quale sarà il risultato del banco di lavoro per loro?
Clark Bao,

@ClarkBao (scusate il ritardo) La memorizzazione di qualsiasi oggetto come chiave utilizzerà l'oggetto hashCode(). Ti dà un intcome chiave.
Matthieu,

47

So che questo è un vecchio post e ci sono un sacco di risposte qui. Ma le risposte sopra sono superficiali e troppo semplificate in termini di suggerimento di una biblioteca. Non esiste una libreria che vada bene attraverso i vari benchmark presentati qui. L'unica conclusione che traggo è se ti preoccupi delle prestazioni e della memoria e hai a che fare con tipi primitivi, vale la pena guardare le alternative non jdk.

Ecco un'analisi più solida, in termini di meccanica di riferimento e librerie coperte. Questo è un thread nella lista degli sviluppatori mahout.

Le biblioteche coperte sono

  • HPPC
  • Raccolta
  • FastUtil
  • Mahout (Colt)
  • Collezioni Java

Aggiornamento giugno 2015 : sfortunatamente, i benchmark originali non sono più disponibili e inoltre sono un po 'obsoleti. Ecco alcuni benchmark abbastanza recenti (gennaio 2015) fatti da qualcun altro. Non è così completo né ha gli strumenti esplorativi interattivi come il link originale.


1
Grazie. Questo è stato molto utile .. considerando l'importanza della domanda è difficile credere che nessuna delle altre risposte (tranne quella di Duckman) in realtà risponda a questa domanda.
Dexter,

20

Come hanno notato altri commentatori, la definizione di "efficiente" lancia una vasta rete. Tuttavia nessuno ha ancora menzionato la libreria Javolution .

Alcuni dei punti salienti:

  • Le classi Javolution sono veloci, molto veloci (ad es. Inserimento / cancellazione del testo in O [Log (n)] invece di O [n] per StringBuffer / StringBuilder standard).
  • Tutte le classi Javolution sono conformi in tempo reale e hanno un comportamento altamente deterministico (nel range dei microsecondi). Inoltre (a differenza della libreria standard), Javolution è sicuro RTSJ (nessun scontro di memoria o perdita di memoria se utilizzato con l'estensione Java Real-Time).
  • Le classi di raccolta in tempo reale di Javolution (mappa, elenco, tabella e set) possono essere utilizzate al posto della maggior parte delle classi di raccolta standard e forniscono funzionalità aggiuntive.
  • Le raccolte Javolution forniscono garanzie di concorrenza per facilitare l'implementazione di algoritmi paralleli.

La distribuzione Javolution include una suite di benchmark in modo da poter vedere come si sovrappongono ad altre librerie / raccolte incorporate.


16

Alcune librerie di raccolta da considerare:

Vorrei prima di tutto raggiungere la biblioteca della collezione JDK. Copre le cose più comuni che devi fare ed è ovviamente già a tua disposizione.

Google Collections è probabilmente la migliore libreria di alta qualità al di fuori di JDK. È molto usato e ben supportato.

Le collezioni Apache Commons sono più vecchie e soffrono un po 'del problema "troppi cuochi" ma hanno anche molte cose utili.

Trove ha raccolte molto specializzate per casi come chiavi / valori primitivi. In questi giorni troviamo che sui moderni JDK e con le raccolte Java 5+ e i casi d'uso simultaneo, le collezioni JDK superano anche le collezioni specializzate Trove.

Se hai casi d'uso con concorrenza davvero elevata, dovresti assolutamente provare cose come NonBlockingHashMap nella libreria su larga scala, che è un'implementazione senza blocco e che può calpestare ConcurrentHashMap se hai il caso d'uso giusto per questo.


7
"In questi giorni troviamo che sui moderni JDK e con le raccolte Java 5+ e i casi d'uso simultaneo, le collezioni JDK superano anche le collezioni specializzate Trove." Ingannevole: non ho mai visto un micro-benchmark in cui l'archiviazione / il recupero di tipi primitivi in ​​una classe specializzata di raccolta di primitive come Trove non ha superato le classi di raccolta JDK sia nell'utilizzo della memoria che nel tempo della CPU. Se stai usando oggetti (e non tipi primitivi), sarei d'accordo con Alex, preoccuparsi della raccolta impl non è un grosso problema.
Riyad Kalla,

2
Questa affermazione si basava su un uso intenso del mondo reale (che prenderò in mano un micro-benchmark ogni giorno) di vari impls di raccolta dove prima avevamo bisogno di una collezione Trove ma ora eravamo in grado di estrarla. Gli aggiornamenti di JDK 6 in ritardo (verso la fine del 2009) in realtà hanno fornito codice personalizzato per chiavi di mappe comuni come Integer che hanno sostanzialmente migliorato alcuni degli usi più comuni.
Alex Miller,

1
Alex, non dubito nei tuoi specifici casi d'uso che tirare fuori collezioni primitive e andare con le collezioni JDK sia stato abbastanza veloce, ma agitando la mano attraverso il paesaggio che è collezioni e dicendo "Tutti quelli che passano, è abbastanza veloce! " non è preciso. Se sto lavorando su un motore di gioco 2D, l'overhead di boxe / unboxing i miei tipi primitivi è costantemente misurabile. Se sto lavorando su un'API REST allora no, probabilmente non rende affatto misurabile una differenza rispetto a operazioni molto più costose come l'I / O HTTP. Mi sono appena sentito obbligato a quantificare il tuo post.
Riyad Kalla,

4
Non credo che chiunque legga questo dovrebbe ascoltare nessuno dei due. Dovrebbero testare il proprio caso d'uso e vedere quali sono le migliori prestazioni. I miei commenti si basano sui test delle prestazioni abbastanza aggressivi del mio team con una varietà di librerie. YMMV.
Alex Miller,

2
Sono d'accordo con @Riyad. Sto scrivendo una suite di automi finiti ad alte prestazioni e l'ho implementata con Trove e Java Collections Framework (ultimo aggiornamento di jdk 6). Ha superato le prestazioni alla grande. Nell'ordine di decine di volte migliori sia nella velocità di calcolo che nel consumo di memoria.
Nico Huysamen,

6

java.util

Ci scusiamo per la risposta ovvia, ma per la maggior parte degli usi, le raccolte Java predefinite sono più che sufficienti.


4
Per usi di base, sì. Ma penso che il framework manchi alcune funzionalità di base e avanzate (come raccolte immutabili, filtri, multimappa, ecc.) Ed è qui che (per esempio) arriva Google Collections
Jorn

1
Penso che questa risposta non risponda al punto. Il JCF era probabilmente fantastico nel 2002 quando le persone non usavano Java per molto. Sfortunatamente non è invecchiato bene, soprattutto se paragonato al supporto delle raccolte da altre lingue JVM.
Ted Pennings,

3
-1 La domanda è "più efficiente per la memorizzazione di int" e qualsiasi esempio citato è migliore di java.util
kommradHomer,



3

ConcurrentHashMap e il java.util.concurrentpacchetto devono essere menzionati se si prevede di utilizzare HashMap in più thread. viene considerato un ingombro di memoria ridotto, poiché fa parte del java standard.


3

Dipende da come definiamo "efficiente".

Ogni struttura di dati ha il proprio comportamento Big-Oh per la lettura, la scrittura, l'iterazione, l'impronta di memoria, ecc. È probabile che un elenco collegato in una libreria sia uguale a qualsiasi altro. E una mappa hash sarà più veloce per la lettura di O (1) rispetto a un elenco collegato O (n).

Ma quando leggo le risposte alla domanda "Le librerie Java gratuite più utili?" Ho notato che difficilmente viene menzionato.

Non sembra "il più efficiente". Sembra "il più popolare" per me.

Solo un feedback: non ne ho mai sentito parlare e non conosco nessuno che l'abbia usato. Le raccolte integrate in JDK, Google o Apache Commons sono famose per me.


3

Trove offre alcuni vantaggi.

  • footprint di memoria ridotto, non utilizza oggetti Map.Entry
  • puoi usare le strategie di hash invece delle chiavi per le mappe, questo fa risparmiare memoria e significa che non è necessario definire una nuova chiave ogni volta che vuoi memorizzare nella cache un oggetto su un nuovo set dei suoi attributi
  • ha tipi di raccolta primitivi
  • penso che abbia una qualche forma di iteratore interno

Detto questo, molto è stato fatto per migliorare le raccolte jdk da quando è stato scritto trove.

Sono le strategie di hashing che lo rendono attraente per me ... Google per trove e leggi la loro panoramica.


2

Se si desidera archiviare milioni di record in una tabella hash, è probabile che si verifichino problemi di memoria. Questo mi è successo quando ho provato a creare una mappa con 2,3 milioni di oggetti String, ad esempio. Sono andato con BerkeleyDB , che è molto maturo e si comporta bene. Hanno un'API Java che avvolge l'API Collections, quindi puoi facilmente creare mappe arbitrariamente grandi con un ingombro di memoria molto ridotto. L'accesso sarà più lento (poiché è memorizzato sul disco).

Domanda di follow-up : esiste una biblioteca decente (ed efficiente), ben mantenuta, per collezioni immutabili? Clojure ha un eccellente supporto per questo, e sarebbe bello avere qualcosa di simile per Java.


1
Le raccolte di Google aggiungono raccolte immutabili.
the.duckman
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.