¿Cuál es la biblioteca de colecciones de Java más eficiente? [cerrado]

135

¿Cuál es la biblioteca de colecciones de Java más eficiente?

Hace unos años, hice un montón de Java y tuve la impresión de que tesoro es la mejor (más eficiente) implementación de Colecciones de Java. Pero cuando leí las respuestas a la pregunta: " La mayoría de las bibliotecas Java libres útiles? " Noté que tesoro apenas se menciona. Entonces, ¿qué biblioteca de colecciones de Java es mejor ahora?

ACTUALIZACIÓN: para aclarar, en su mayoría quiero saber qué biblioteca usar cuando tengo que almacenar millones de entradas en una tabla hash, etc. (necesito un tiempo de ejecución pequeño y huella de memoria).

Franco
fuente
¿Cuáles son las claves y los valores en esta tabla? Si no son primitivos, ¿qué tiene de malo el HashMap normal, etc.?
Jon Skeet
Para un mapa muy grande, es posible que desee una implementación de prueba o incluso en línea como una tabla de base de datos.
Tom Hawtin - tackline
1
Curiosamente, no veo mención de Colt aquí, que posteriormente se incluyó en Mahout.
smartnut007
44
Vale la pena mencionar una biblioteca de colecciones muy agradable: colecciones GS (github.com/goldmansachs/gs-collections). Cuenta con excelente documentación y un exhaustivo conjunto de colecciones mutables e inmutables
Piotr Kochański

Respuestas:

73

Después de la inspección, parece que Trove es solo una biblioteca de colecciones para tipos primitivos; no es como si estuviera agregando mucha funcionalidad sobre las colecciones normales en el JDK.

Personalmente (y soy parcial), me encanta la guayaba (incluido el antiguo proyecto Google Java Collections). Hace que las tareas (incluidas las colecciones) sean mucho más fáciles, de una manera que sea al menos razonablemente eficiente. Dado que las operaciones de recopilación rara vez forman un cuello de botella en mi código (en mi experiencia) esto es "mejor" que una API de recopilación que puede ser más eficiente pero no hace que mi código sea legible.

Dado que la superposición entre Trove y Guava es prácticamente nula, tal vez podría aclarar lo que realmente está buscando en una biblioteca de colecciones.

Jon Skeet
fuente
3
@Andreas: No puedo decir que estoy de acuerdo. No es que sea un "uno u otro" escenario: uso las colecciones regulares (con ayudantes como la clase Listas) y luego uso Iterables, etc. cuando lo necesito. Utiliza la complejidad solo cuando te ayude.
Jon Skeet
10
después de leer mi propio comentario varios meses después de usar GC por completo, no estoy de acuerdo con mi opinión anterior y estoy totalmente de acuerdo con la suya. use los métodos / clases auxiliares ampliamente, hacen que gran parte del código sea más legible y seguro.
Andreas Petersson el
1
@Andreas: Gracias por volver y decirlo. Me alegra saber que GJC está ayudando :)
Jon Skeet,
2
Hola, Jon, Google Java Collections ahora es Guava . Es posible que desee actualizar su publicación para futuras referencias :)
Artur Czajka
1
He trabajado en bastantes proyectos de uso intensivo de datos en los que las colecciones fueron un gran cuello de botella. Las colecciones de Java son terriblemente ineficientes (memoria y velocidad) especialmente si almacenan primitivas.
Jay Askren
104

La pregunta es (ahora) sobre el almacenamiento de muchos datos, que se pueden representar usando tipos primitivos como int, en un Mapa. Algunas de las respuestas aquí son muy engañosas en mi opinión. A ver por qué.

Modifiqué el punto de referencia de tesoro para medir tanto el tiempo de ejecución como el consumo de memoria. También agregué PCJ a este punto de referencia, que es otra biblioteca de colecciones para tipos primitivos (lo uso ampliamente). El punto de referencia del tesoro 'oficial' no compara IntIntMaps con los de la Colección Java Map<Integer, Integer>, probablemente almacenar Integersy almacenar intsno es lo mismo desde un punto de vista técnico. Pero a un usuario puede no importarle este detalle técnico, quiere almacenar datos representables de manera intseficiente.

Primero la parte relevante del 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();
     }

Supongo que los datos son primitivos ints, lo que parece cuerdo. Pero esto implica una penalización de tiempo de ejecución para java util, debido al auto-boxeo, que no es necesario para los marcos de colecciones primitivas.

Los resultados de tiempo de ejecución (sin gc()llamadas, por supuesto) en WinXP, jdk1.6.0_10:

                      100000 operaciones de colocación 100000 contiene operaciones 
colecciones java 1938 ms 203 ms
tesoro 234 ms 125 ms
pcj 516 ms 94 ms

Si bien esto puede parecer drástico, esta no es la razón para usar dicho marco.

La razón es el rendimiento de la memoria. Los resultados para un mapa que contiene 100000 intentradas:

Las colecciones java oscilan entre 6644536 y 7168840 bytes
trove 1853296 bytes
pcj 1866112 bytes

Java Collections necesita más de tres veces la memoria en comparación con los marcos de colección primitivos. Es decir, puede mantener tres veces más datos en la memoria, sin recurrir al disco IO que reduce el rendimiento del tiempo de ejecución por magnitud. Y esto importa. Lea la escalabilidad alta para descubrir por qué.

En mi experiencia, el alto consumo de memoria es el mayor problema de rendimiento con Java, que por supuesto también resulta en un peor rendimiento en tiempo de ejecución. Los marcos de colección primitivos realmente pueden ayudar aquí.

Entonces: No, java.util no es la respuesta. Y "agregar funcionalidad" a las colecciones de Java no es el punto cuando se pregunta sobre la eficiencia. Además, las colecciones modernas de JDK no "superan incluso a las colecciones especializadas de Trove".

Descargo de responsabilidad: el punto de referencia aquí está lejos de ser completo, ni es perfecto. Está destinado a llevar a casa el punto, que he experimentado en muchos proyectos. Las colecciones primitivas son lo suficientemente útiles como para tolerar la API sospechosa, si trabaja con muchos datos.

the.duckman
fuente
3
En realidad, creo que tu respuesta es engañosa. Almacenar ints vs Integers es muy diferente, y muy probablemente sea la razón principal del mayor uso de memoria. Estoy de acuerdo en que un marco de colección de tipos sin formato podría ser útil, pero no hace que trove o pcj sean "mejores" que java.util.
Jorn
22
La pregunta es sobre el almacenamiento de datos int de manera eficiente. No se trata de almacenar números enteros. Para esta tarea, trove / pcj son más eficientes, como traté de mostrar. Usar números enteros impone ineficiencias de tiempo de ejecución y memoria. Como java.util no permite el uso de primitivas, no es la mejor opción para esta tarea.
the.duckman
2
(para la comunidad rusa) aquí va otro punto de referencia: total-holywar.blogspot.com/2011/07/…
dma_k
No estoy seguro si no usamos int como clave, solo String normal. ¿Cuál será el resultado del banco de trabajo para ellos?
Clark Bao
@ClarkBao (perdón por llegar tarde) Almacenar cualquier objeto como clave usará el objeto hashCode(). Te da una intclave.
Matthieu
47

Sé que esta es una publicación antigua y hay un montón de respuestas aquí. Pero, las respuestas anteriores son superficiales y demasiado simplificadas en términos de sugerir una biblioteca. No hay una biblioteca única que funcione bien en los diversos puntos de referencia presentados aquí. La única conclusión que obtengo es que si te importa el rendimiento y la memoria y específicamente tratar con tipos primitivos, vale la pena mirar las alternativas no jdk.

Aquí hay un análisis más sólido, en términos de mecánica de referencia y las bibliotecas cubiertas. Este es un hilo en la lista de desarrolladores de mahout.

Las bibliotecas cubiertas son

  • HPPC
  • Trove
  • FastUtil
  • Mahout (potro)
  • Colecciones Java

Actualización de junio de 2015 : Desafortunadamente, los puntos de referencia originales ya no están disponibles y además están un poco desactualizados. Aquí hay un punto de referencia bastante reciente (enero de 2015) realizado por otra persona. No es tan completo ni tiene las herramientas exploratorias interactivas como el enlace original.

smartnut007
fuente
1
Gracias. Esto fue muy útil ... teniendo en cuenta la importancia de la pregunta, es difícil creer que ninguna de las otras respuestas (aparte de la de The Duckman) realmente responda esta pregunta.
Dexter
20

Como han notado otros comentaristas, la definición de "eficiente" arroja una amplia red. Sin embargo, nadie ha mencionado aún la biblioteca Javolution .

Algunos de los aspectos más destacados:

  • Las clases de javolución son rápidas, muy rápidas (por ejemplo, inserción / eliminación de texto en O [Log (n)] en lugar de O [n] para StringBuffer / StringBuilder estándar).
  • Todas las clases de Javolution son compatibles en tiempo real y tienen un comportamiento altamente determinista (en el rango de microsegundos). Además (a diferencia de la biblioteca estándar), Javolution es seguro para RTSJ (sin conflicto de memoria o pérdida de memoria cuando se usa con la extensión Java Real-Time).
  • Las clases de recopilación en tiempo real de Javolution (mapa, lista, tabla y conjunto) se pueden utilizar en lugar de la mayoría de las clases de recopilación estándar y proporcionan una funcionalidad adicional.
  • Las colecciones Javolution proporcionan garantías de concurrencia para facilitar la implementación de algoritmos paralelos.

La distribución de Javolution incluye un conjunto de pruebas de referencia para que pueda ver cómo se comparan con otras bibliotecas / colecciones incorporadas.

sstock
fuente
16

Algunas bibliotecas de colección a tener en cuenta:

En primer lugar, buscaría la biblioteca de la colección JDK. Cubre las cosas más comunes que debe hacer y, obviamente, ya está disponible para usted.

Google Collections es probablemente la mejor biblioteca de alta calidad fuera del JDK. Es muy utilizado y bien soportado.

Apache Commons Collections es más antiguo y sufre un poco del problema de "demasiados cocineros", pero también tiene muchas cosas útiles.

Trove tiene colecciones muy especializadas para casos como claves / valores primitivos. En estos días encontramos que en los JDK modernos y con las colecciones Java 5+ y los casos de uso concurrente, las colecciones JDK superan incluso a las colecciones especializadas de Trove.

Si tiene casos de uso de concurrencia realmente alta, definitivamente debería consultar cosas como NonBlockingHashMap en la biblioteca de gran escala, que es una implementación sin bloqueo y puede pisotear ConcurrentHashMap si tiene el caso de uso adecuado para ello.

Alex Miller
fuente
77
"En estos días encontramos que en los JDK modernos y con las colecciones Java 5+ y los casos de uso concurrente, las colecciones JDK superan incluso a las colecciones especializadas de Trove". Engañoso: nunca he visto un micro punto de referencia en el que almacenar / recuperar tipos primitivos en una clase especializada de colección primitiva como Trove no haya superado a las clases de colección JDK tanto en el uso de memoria como en el tiempo de CPU. Sin embargo, si está utilizando objetos (y no tipos primitivos), estaría de acuerdo con Alex, preocuparse por la recopilación impl no es un gran problema.
Riyad Kalla
2
Esta declaración se basó en el uso intensivo en el mundo real (que me haré cargo de un micro punto de referencia cualquier día) de varias implicaciones de colección donde antes necesitábamos una colección de Trove pero ahora pudimos sacarla. Las últimas actualizaciones de JDK 6 (alrededor de finales de 2009) en realidad proporcionaron un código personalizado para claves de mapa comunes como Integer que han mejorado sustancialmente algunos de los usos más comunes.
Alex Miller
1
Alex, no dudo en tus casos de uso específicos que sacar colecciones primitivas e ir con colecciones JDK fue lo suficientemente rápido, pero moviendo tu mano por el paisaje que son colecciones y diciendo "¡Todos ustedes que pasan, es lo suficientemente rápido! " no es exacto Si estoy trabajando en un motor de juego 2D, la sobrecarga del boxeo / desempaquetado de mis tipos primitivos constantemente es considerablemente costosa. Si estoy trabajando en una API REST, entonces no, probablemente no haga una diferencia medible en absoluto con respecto a operaciones mucho más costosas como la E / S HTTP. Me sentí obligado a cuantificar tu publicación, eso es todo.
Riyad Kalla
44
No creo que nadie que lea esto deba escucharnos a ninguno de los dos. Deben probar su propio caso de uso y ver cuál tiene el mejor rendimiento. Mis comentarios se basan en las pruebas de rendimiento bastante agresivas de mi equipo con una variedad de bibliotecas. YMMV.
Alex Miller
2
Estoy de acuerdo con @Riyad. Estoy escribiendo un conjunto de autómatas finitos de alto rendimiento y lo he implementado tanto con Trove como con Java Collections Framework (última actualización de jdk 6). Trove supera a lo grande. En el orden de decenas de veces mejor tanto en velocidad de cómputo como en consumo de memoria.
Nico Huysamen
6

java.util

Perdón por la respuesta obvia, pero para la mayoría de los usos, las colecciones de Java predeterminadas son más que suficientes.

Yuval Adam
fuente
44
Para usos básicos, sí. Pero creo que el marco pierde algunas características básicas y avanzadas (como colecciones inmutables, filtros, mapas múltiples, etc.) y ahí es donde (por ejemplo) entra Google Collections
Jorn
1
Creo que esta respuesta pierde el punto. El JCF fue probablemente increíble en 2002 cuando la gente no usaba Java por mucho tiempo. Desafortunadamente, no ha envejecido bien, especialmente en comparación con el soporte de colecciones de otros lenguajes JVM.
Ted Pennings
3
-1 La pregunta es "más eficiente para almacenar int" y cualquier ejemplo mencionado es mejor que java.util
kommradHomer
6

Para almacenar millones Stringen un mapa, visite http://code.google.com/p/flatmap

akuhn
fuente
3
+1 ¿Puedes presentar cómo se mejoró?
Clark Bao
1
Debería haber publicaciones en el blog del autor de flatmap en algún lugar de Internet.
akuhn
3

java.util.concurrentDeben mencionarse ConcurrentHashMap y el paquete, si planea usar HashMap en varios subprocesos. Se asume una pequeña huella de memoria, ya que esto es parte de Java estándar.

Andreas Petersson
fuente
3

Depende de cómo definimos "eficiente".

Cada estructura de datos tiene su propio comportamiento Big-Oh para leer, escribir, iterar, huella de memoria, etc. Es probable que una lista vinculada en una biblioteca sea la misma que en cualquier otra. Y un mapa hash será más rápido para leer O (1) que una lista vinculada O (n).

Pero cuando leí las respuestas a la pregunta "¿Las bibliotecas Java gratuitas más útiles?" Me di cuenta de que el tesoro apenas se menciona.

Esto no suena como "más eficiente". A mí me parece el "más popular".

Solo algunos comentarios: nunca he oído hablar de él, y no conozco a nadie que lo haya usado. Las colecciones integradas en JDK, Google o Apache Commons son conocidas por mí.

duffymo
fuente
3

Trove ofrece algunas ventajas.

  • huella de memoria más pequeña, no utiliza Map.Entry objetos
  • puede usar estrategias hash en lugar de claves para mapas, esto ahorra memoria y significa que no necesita definir una nueva clave cada vez que desea almacenar en caché un objeto en un nuevo conjunto de sus atributos
  • tiene tipos de colección primitivos
  • piensa que tiene alguna forma de iterador interno

Dicho esto, se ha hecho mucho para mejorar las colecciones jdk desde que se escribió tesoro.

Sin embargo, son las estrategias de hash lo que me hacen atractivo ... Google para buscar y leer su resumen.

duffymo
fuente
2

Si desea almacenar millones de registros en una tabla hash, es probable que tenga problemas de memoria. Esto me sucedió cuando intenté crear un mapa con 2,3 millones de objetos String, por ejemplo. Fui con BerkeleyDB , que es muy maduro y funciona bien. Tienen una API de Java que envuelve la API de Colecciones, por lo que puede crear fácilmente mapas arbitrariamente grandes con muy poca huella de memoria. Sin embargo, el acceso será más lento (ya que está almacenado en el disco).

Pregunta de seguimiento : ¿existe una biblioteca decente (y eficiente), bien mantenida, para colecciones inmutables? Clojure tiene un excelente soporte para esto, y sería bueno tener algo similar para Java.

fred-o
fuente
1
Las colecciones de Google agregan colecciones inmutables.
the.duckman