Quelle est la bibliothèque de collections Java la plus efficace? [fermé]

135

Quelle est la bibliothèque de collections Java la plus efficace?

Il y a quelques années, j'ai fait beaucoup de Java et j'avais l'impression à l'époque que le trésor est la meilleure implémentation (la plus efficace) des collections Java. Mais quand j'ai lu les réponses à la question « Les bibliothèques Java gratuites les plus utiles? », J'ai remarqué que le trésor est à peine mentionné. Alors, quelle bibliothèque Java Collections est la meilleure maintenant?

MISE À JOUR: Pour clarifier, je veux surtout savoir quelle bibliothèque utiliser lorsque je dois stocker des millions d'entrées dans une table de hachage, etc. (besoin d'un petit temps d'exécution et d'une petite empreinte mémoire).

Franc
la source
Quelles sont les clés et les valeurs de ce tableau? S'ils ne sont pas des primitives, qu'est-ce qui ne va pas avec le HashMap normal, etc.?
Jon Skeet
Pour une très grande carte, vous souhaiterez peut-être une implémentation de détection, ou même intégrée comme une table de base de données.
Tom Hawtin - Tackline
1
Fait intéressant, je ne vois aucune mention de Colt ici qui a ensuite été subsumée dans Mahout.
smartnut007
4
Il vaut la peine de mentionner une très belle bibliothèque de collections - GS collections (github.com/goldmansachs/gs-collections). Il a une excellente documentation et un ensemble exhaustif de colections mutables et immuables
Piotr Kochański

Réponses:

73

D'après l'inspection, il semble que Trove n'est qu'une bibliothèque de collections pour les types primitifs - ce n'est pas comme si elle était censée ajouter beaucoup de fonctionnalités par rapport aux collections normales du JDK.

Personnellement (et je suis partial) j'adore Guava (y compris l'ancien projet Google Java Collections). Cela facilite grandement diverses tâches (y compris les collections), d'une manière au moins raisonnablement efficace. Étant donné que les opérations de collecte forment rarement un goulot d'étranglement dans mon code (d'après mon expérience), c'est "mieux" qu'une API de collections qui peut être plus efficace mais ne rend pas mon code aussi lisible.

Étant donné que le chevauchement entre Trove et Guava est quasiment nul, vous pourriez peut-être clarifier ce que vous recherchez réellement dans une bibliothèque de collections.

Jon Skeet
la source
3
@Andreas: Je ne peux pas dire que je suis d'accord. Non pas que ce soit un scénario «l'un ou l'autre» - j'utilise les collections régulières (avec des helpers comme la classe Lists) et j'utilise ensuite Iterables, etc. quand j'en ai besoin. N'utilisez la complexité que lorsqu'elle vous aide.
Jon Skeet
10
après avoir lu mon propre commentaire plusieurs mois après avoir largement utilisé GC - je ne suis pas d'accord avec mon opinion passée et je suis entièrement d'accord avec le vôtre. utilisent largement les méthodes / classes d'assistance, elles rendent une grande partie du code plus lisible et plus sûre.
Andreas Petersson
1
@Andreas: Merci d'être revenu et de l'avoir dit - Je suis heureux d'apprendre que GJC aide :)
Jon Skeet
2
Hé, Jon, Google Java Collections s'appelle désormais Guava . Vous voudrez peut-être mettre à jour votre message pour de futures références :)
Artur Czajka
1
J'ai travaillé sur pas mal de projets gourmands en données où les collections constituaient un énorme goulot d'étranglement. Les collections Java sont terriblement inefficaces (à la fois en mémoire et en vitesse), surtout si elles stockent des primitives.
Jay Askren
104

La question est (maintenant) de stocker beaucoup de données, qui peuvent être représentées à l'aide de types primitifs comme int, dans une carte. Certaines des réponses ici sont très trompeuses à mon avis. Voyons pourquoi.

J'ai modifié le benchmark de trove pour mesurer à la fois le temps d'exécution et la consommation de mémoire. J'ai également ajouté PCJ à ce benchmark, qui est une autre bibliothèque de collections pour les types primitifs (je l'utilise beaucoup). Le benchmark «officiel» de trove ne compare pas IntIntMaps à Java Collection Map<Integer, Integer>, le stockage Integerset le stockage intsne sont probablement pas les mêmes d'un point de vue technique. Mais un utilisateur peut ne pas se soucier de ce détail technique, il souhaite stocker des données représentables avec intsefficacité.

D'abord la partie pertinente du code:

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();
     }

Je suppose que les données sont primitives ints , ce qui semble raisonnable. Mais cela implique une pénalité d'exécution pour java util, à cause de l'auto-boxing, qui n'est pas nécessaire pour les frameworks de collections primitives.

Les résultats d'exécution (sans gc() appels, bien sûr) sur WinXP, jdk1.6.0_10:

                      100000 opérations put 100000 contient des opérations 
collections java 1938 ms 203 ms
trove 234 ms 125 ms
pcj 516 ms 94 ms

Bien que cela puisse déjà sembler drastique, ce n'est pas la raison d'utiliser un tel cadre.

La raison est la performance de la mémoire. Les résultats pour une carte contenant 100000int entrées:

les collections java oscille entre 6644536 et 7168840 octets
trove 1853296 octets
pcj 1866112 octets

Les collections Java ont besoin de plus de trois fois la mémoire par rapport aux frameworks de collection primitifs. C'est-à-dire que vous pouvez conserver trois fois plus de données en mémoire, sans recourir aux E / S de disque, ce qui réduit considérablement les performances d'exécution. Et cela compte. Lisez la haute évolutivité pour découvrir pourquoi.

D'après mon expérience, la consommation de mémoire élevée est le plus gros problème de performances avec Java, ce qui entraîne bien sûr également une dégradation des performances d'exécution. Les frameworks de collection primitifs peuvent vraiment aider ici.

Donc: Non, java.util n'est pas la réponse. Et «l'ajout de fonctionnalités» aux collections Java n'est pas le but lorsque l'on se questionne sur l'efficacité. De plus, les collections JDK modernes ne " surpassent pas même les collections spécialisées Trove".

Avertissement: le benchmark ici est loin d'être complet, ni parfait. Il vise à faire ressortir le point que j'ai vécu dans de nombreux projets. Les collections primitives sont suffisamment utiles pour tolérer une API louche - si vous travaillez avec beaucoup de données.

le canard
la source
3
En fait, je pense que votre réponse est trompeuse. Le stockage des entiers par rapport aux entiers est très différent, et probablement la principale raison de l'utilisation accrue de la mémoire. Je suis d'accord qu'un framework de collecte de types bruts pourrait être utile, mais il ne rend pas trove ou pcj "meilleur" que java.util.
Jorn
22
La question est de stocker efficacement les données int. Pas pour stocker des entiers. Pour cette tâche, trove / pcj est plus efficace, comme j'ai essayé de le montrer. L'utilisation d'entiers impose des inefficacités d'exécution et de mémoire. Puisque java.util n'autorise pas l'utilisation de primitives, ce n'est pas le meilleur choix pour cette tâche.
the.duckman
2
(pour la communauté russe) voici une autre référence: total-holywar.blogspot.com/2011/07/…
dma_k
Je ne sais pas si nous n'utilisons pas int comme clé, juste une chaîne normale. Quel sera le résultat de l'atelier pour eux?
Clark Bao
@ClarkBao (désolé d'être en retard) Le stockage d'un objet comme clé utilisera l'objet hashCode(). Cela vous donne une intclé.
Matthieu
47

Je sais que c'est un ancien message et il y a une tonne de réponses ici. Mais, les réponses ci-dessus sont superficielles et simplifiées en termes de suggestion d'une bibliothèque. Il n'y a pas de bibliothèque qui fonctionne bien à travers les divers benchmarks présentés ici. La seule conclusion que je tire est que si vous vous souciez des performances et de la mémoire et que vous vous occupez spécifiquement des types primitifs, cela vaut la peine de regarder les alternatives non jdk.

Voici une analyse plus solide, en termes de mécanique de référence et des bibliothèques couvertes. Ceci est un fil dans la liste des développeurs de mahout.

Les bibliothèques couvertes sont

  • HPPC
  • Trove
  • FastUtil
  • Mahout (Colt)
  • Collections Java

Mise à jour juin 2015 : Malheureusement, les benchmarks d'origine ne sont plus disponibles et en plus c'est un peu obsolète. Voici un benchmark assez récent (janvier 2015) réalisé par quelqu'un d'autre. Il n'est pas aussi complet et ne dispose pas d'outils d'exploration interactifs que le lien d'origine.

smartnut007
la source
1
Je vous remercie. Cela a été très utile. Compte tenu de l'importance de la question, il est difficile de croire qu'aucune des autres réponses (à part celle du canard) ne répond réellement à cette question.
Dexter
20

Comme d'autres commentateurs l'ont remarqué, la définition d '«efficace» jette un large filet. Cependant, personne n'a encore mentionné la bibliothèque Javolution .

Certains des points forts:

  • Les classes Javolution sont rapides, très rapides (par exemple, insertion / suppression de texte dans O [Log (n)] au lieu de O [n] pour StringBuffer / StringBuilder standard).
  • Toutes les classes Javolution sont conformes en temps réel et ont un comportement hautement déterministe (de l'ordre de la microseconde). De plus (contrairement à la bibliothèque standard), Javolution est RTSJ safe (pas de conflit de mémoire ou de fuite de mémoire lorsqu'il est utilisé avec l'extension Java Real-Time).
  • Les classes de collection en temps réel de Javolution (carte, liste, table et ensemble) peuvent être utilisées à la place de la plupart des classes de collection standard et fournissent des fonctionnalités supplémentaires.
  • Les collections Javolution fournissent des garanties de concurrence pour faciliter la mise en œuvre d'algorithmes parallèles.

La distribution Javolution inclut une suite de benchmark afin que vous puissiez voir comment ils se comparent aux autres bibliothèques / collections intégrées.

sstock
la source
16

Quelques bibliothèques de collection à considérer:

Je voudrais avant tout atteindre la bibliothèque de la collection JDK. Il couvre les tâches les plus courantes que vous devez faire et est évidemment déjà disponible pour vous.

Google Collections est probablement la meilleure bibliothèque de haute qualité en dehors du JDK. Il est très utilisé et bien pris en charge.

Apache Commons Collections est plus ancienne et souffre un peu du problème du "trop ​​de cuisiniers", mais contient également beaucoup de choses utiles.

Trove a des collections très spécialisées pour des cas comme les clés / valeurs primitives. Ces jours-ci, nous constatons que sur les JDK modernes et avec les collections Java 5+ et les cas d'utilisation simultanés, les collections JDK surpassent même les collections Trove spécialisées.

Si vous avez des cas d'utilisation de concurrence très élevée, vous devez absolument consulter des éléments tels que NonBlockingHashMap dans la bibliothèque à grande échelle, qui est une implémentation sans verrouillage et peut piétiner ConcurrentHashMap si vous avez le bon cas d'utilisation.

Alex Miller
la source
7
"De nos jours, nous constatons que sur les JDK modernes et avec les collections Java 5+ et les cas d'utilisation simultanés, les collections JDK surpassent même les collections spécialisées Trove." Trompeur - Je n'ai jamais vu de micro-benchmark où le stockage / récupération de types primitifs dans une classe de collection primitive spécialisée comme Trove n'a pas surpassé les classes de collection JDK en termes d'utilisation de la mémoire et de temps CPU. Si vous utilisez des objets (et non des types primitifs), alors je serais d'accord avec Alex, se soucier de la collection impl n'est pas aussi grave.
Riyad Kalla
2
Cette déclaration était basée sur une utilisation intensive dans le monde réel (que je prendrai en charge un micro-benchmark n'importe quel jour) de divers impls de collection où nous avions auparavant besoin d'une collection Trove mais que nous pouvions maintenant la retirer. Les dernières mises à jour du JDK 6 (vers la fin de 2009) ont en fait fourni un code personnalisé pour les clés de carte courantes comme Integer qui ont considérablement amélioré certaines des utilisations les plus courantes.
Alex Miller
1
Alex, je ne doute pas dans vos cas d'utilisation spécifiques que l'extraction des collections primitives et l'utilisation des collections JDK était assez rapide, mais en agitant la main à travers le paysage qu'est les collections et en disant "Tous ceux qui passent, c'est assez rapide! " n'est pas précis. Si je travaille sur un moteur de jeu 2D, les frais généraux de boxer / déballer mes types primitifs sont constamment mesurables. Si je travaille sur une API REST, non, cela ne fait probablement pas du tout une différence mesurable par rapport à des opérations beaucoup plus coûteuses comme les E / S HTTP. Je me suis juste senti obligé de quantifier votre message, c'est tout.
Riyad Kalla
4
Je ne pense pas que quiconque lisant ceci devrait écouter l'un ou l'autre de nous. Ils doivent tester leur propre cas d'utilisation et voir ce qui a les meilleures performances. Mes commentaires sont basés sur les tests de performances assez agressifs de mon équipe avec une variété de bibliothèques. YMMV.
Alex Miller
2
Je suis d'accord avec @Riyad. J'écris une suite d'automates finis hautes performances et je l'ai implémentée avec Trove et Java Collections Framework (dernière mise à jour de jdk 6). Trove surpasse énormément. De l'ordre de dizaines de fois mieux en vitesse de calcul et en consommation de mémoire.
Nico Huysamen
6

java.util

Désolé pour la réponse évidente, mais pour la plupart des utilisations, les collections Java par défaut sont plus que suffisantes.

Yuval Adam
la source
4
Pour les utilisations basiques, oui. Mais je pense que le cadre manque certaines fonctionnalités de base et avancées (comme les collections immuables, les filtres, les multi-cartes, etc.) et c'est là que (par exemple) Google Collections entre en jeu
Jorn
1
Je pense que cette réponse manque le point. Le JCF était probablement génial en 2002 lorsque les gens n'utilisaient pas beaucoup Java. Malheureusement, il n'a pas bien vieilli, en particulier par rapport au support des collections d'autres langages JVM.
Ted Pennings
3
-1 La question est "le plus efficace pour stocker des int" et tout exemple mentionné est meilleur que java.util
kommradHomer
6

Pour stocker des millions de dollars Stringsur une carte, consultez la page http://code.google.com/p/flatmap

Akuhn
la source
3
+1 Pouvez-vous présenter comment il a été amélioré?
Clark Bao
1
Il devrait y avoir des articles de blog de l'auteur de flatmap quelque part sur Internet.
akuhn
4

Je suis développeur de happy-collections de happy-collections sur source-forge

  1. Collections basées sur des événements
  2. Non modifiable
  3. SortedList
  4. Cache
Andreas Hollmann
la source
3

ConcurrentHashMap ainsi que le java.util.concurrentpackage doivent être mentionnés, si vous prévoyez d'utiliser le HashMap dans plusieurs threads. une faible empreinte mémoire est évaluée, car cela fait partie de java standard.

Andreas Petersson
la source
3

Cela dépend de la façon dont nous définissons «efficace».

Chaque structure de données a son propre comportement Big-Oh pour la lecture, l'écriture, l'itération, l'empreinte mémoire, etc. Une liste chaînée dans une bibliothèque est susceptible d'être la même que toute autre. Et une carte de hachage sera plus rapide pour lire O (1) qu'une liste chaînée O (n).

Mais quand j'ai lu les réponses à la question "Les bibliothèques Java gratuites les plus utiles?" J'ai remarqué que le trésor est à peine mentionné.

Cela ne semble pas être «le plus efficace». Cela me semble "le plus populaire".

Juste quelques commentaires - je n'en ai jamais entendu parler, et je ne connais personne qui l'ait utilisé. Les collections intégrées au JDK, Google ou Apache Commons me sont bien connues.

duffymo
la source
3

Trove offre quelques avantages.

  • plus petite empreinte mémoire, il n'utilise pas les objets Map.Entry
  • vous pouvez utiliser des stratégies de hachage à la place des clés pour les cartes, cela économise de la mémoire et signifie que vous n'avez pas besoin de définir une nouvelle clé chaque fois que vous souhaitez mettre en cache un objet sur un nouvel ensemble de ses attributs
  • il a des types de collection primitifs
  • pense qu'il a une forme d'itérateur interne

Cela dit, beaucoup a été fait pour améliorer les collections jdk depuis l'écriture de trove.

Ce sont les stratégies de hachage qui me plaisent cependant ... Google pour trove et lisez leur aperçu.

duffymo
la source
2

Si vous souhaitez stocker des millions d'enregistrements dans une table de hachage, il est probable que vous rencontriez des problèmes de mémoire. Cela m'est arrivé lorsque j'ai essayé de créer une carte avec 2,3 millions d'objets String, par exemple. Je suis allé avec BerkeleyDB , qui est très mature et fonctionne bien. Ils ont une API Java qui encapsule l'API Collections, de sorte que vous pouvez facilement créer des cartes arbitrairement grandes avec très peu d'espace mémoire. L'accès sera cependant plus lent (car il est stocké sur disque).

Question complémentaire : existe-t-il une bibliothèque décente (et efficace), bien entretenue, pour les collections immuables? Clojure a un excellent support pour cela, et ce serait bien d'avoir quelque chose de similaire pour Java.

fred-o
la source
1
Les collections Google ajoutent des collections immuables.
the.duckman