¿Qué es un buen marco de colecciones persistentes para usar en Java?

Por colecciones persistentes me refiero a colecciones como las de clojure.

Por ejemplo, tengo una lista con los elementos (a, b, c). Con una lista normal, si agrego d, mi lista original tendrá (a, b, c, d) como sus elementos. Con una lista persistente, cuando llamo list.add (d), obtengo una nueva lista, sosteniendo (a, b, c, d). Sin embargo, la implementación intenta compartir elementos entre la lista siempre que sea posible, por lo que es mucho más eficiente en cuanto a la memoria que simplemente devolver una copia de la lista original. También tiene la ventaja de ser inmutable (si tengo una referencia a la lista original, siempre devolverá los 3 elementos originales).

Todo esto se explica mucho mejor en otros lugares (por ejemplo, http://en.wikipedia.org/wiki/Persistent_data_structure ).

De todos modos, mi pregunta es … ¿cuál es la mejor biblioteca para proporcionar esta funcionalidad para usar en Java? ¿Puedo usar las colecciones de clojure de alguna manera (otras que mediante el uso directo de clojure)?

Simplemente usa los de Clojure directamente. Si bien es posible que no desee utilizar el idioma en sí mismo, puede utilizar las colecciones persistentes directamente ya que todas son solo clases Java.

import clojure.lang.PersistentHashMap; import clojure.lang.IPersistentMap; IPersistentMap map = PersistentHashMap.create("key1", "value1"); assert map.get("key1").equals("value1"); IPersistentMap map2 = map.assoc("key1", "value1"); assert map2 != map; assert map2.get("key1").equals("value1"); 

(descargo de responsabilidad: en realidad no he comstackdo ese código 🙂

El inconveniente es que las colecciones no están escritas, es decir, no hay generics con ellas.

¿Qué hay de pcollections ?

También puede consultar la implementación de Clojure de colecciones persistentes ( PersistentHashMap , por ejemplo).

Estaba buscando un marco de recostackción persistente delgado, “amigable” de Java y tomé TotallyLazy y PCollections mencionados en este hilo para una prueba, porque me parecieron muy prometedores.

Ambos proporcionan interfaces simples razonables para manipular listas persistentes:

 // TotallyLazy PersistentList original = PersistentList.constructors.empty(String.class); PersistentList modified = original.append("Mars").append("Raider").delete("Raider"); // PCollections PVector original = TreePVector.empty(); PVector modified = original.plus("Mars").plus("Raider").minus("Raider"); 

Tanto PersistentList como PVector amplían java.util.List , por lo que ambas bibliotecas deberían integrarse bien en un entorno existente.

Sin embargo, resulta que TotallyLazy se encuentra con problemas de rendimiento cuando se trata de listas más grandes (como ya se mencionó en un comentario anterior de @levantpied). En mi MacBook Pro (finales de 2013) insertar 100.000 elementos y devolver la lista inmutable tomó TotallyLazy ~ 2000ms, mientras que PCollections terminó dentro de ~ 120ms.

Mis casos de prueba (simples) están disponibles en Bitbucket , si alguien quiere mirarlos más a fondo.

https://github.com/andrewoma/dexx es un puerto de las colecciones persistentes de Scala a Java. Incluye:

  • Set, SortedSet, Map, SortedMap y Vector
  • Adaptadores para ver las colecciones persistentes como equivalentes java.util
  • Ayudantes para una construcción fácil

Puede querer ver clj-ds . No lo he usado, pero parece prometedor. Basado en el archivo de proyectos, extrajo las estructuras de datos de Clojure 1.2.0.

La Java funcional implementa una lista persistente, lista diferida, conjunto, mapa y árbol. Puede haber otros, pero estoy siguiendo la información en la página principal del sitio.

También me interesa saber cuál es la mejor biblioteca de estructura de datos persistente para Java. Mi atención se dirigió a Java funcional porque se menciona en el libro, Progtwigción funcional para desarrolladores de Java .

Hay una pcollections (Colecciones persistentes) que puede usar:

http://code.google.com/p/pcollections/

Paguro proporciona versiones de seguridad de tipos de las colecciones reales de Clojure para usar en Java 8+. Incluye: List (Vector), HashMap, TreeMap, HashSet y TreeSet. Se comportan exactamente de la manera que especifique en su pregunta y se han adaptado cuidadosamente a las interfaces de colecciones java.util existentes para obtener la máxima compatibilidad con Java de tipo seguro. También son un poco más rápidos que PCollections .

Codificando su ejemplo en Paguro se ve así:

 // List with the elements (a,b,c) ImList list = vec(a,b,c); // With a persistent list, when I call list.add(d), // I get back a new list, holding (a,b,c,d) ImList newList = list.append(d); list.size(); // still returns 3 newList.size(); // returns 4 

Tu dijiste,

La implementación intenta compartir elementos entre la lista siempre que sea posible, por lo que es mucho más eficiente en cuanto a la memoria y más rápido que simplemente devolver una copia de la lista original. También tiene la ventaja de ser inmutable (si tengo una referencia a la lista original, siempre devolverá los 3 elementos originales).

Sí, así es exactamente como se comporta. Daniel Spiewak explica la velocidad y la eficiencia de estas colecciones mucho mejor de lo que pude.

En la misma línea que Cornelius Mund, Pure4J conecta las colecciones Clojure a Java y agrega compatibilidad con Generics.

Sin embargo, Pure4J tiene como objective introducir la semántica de progtwigción pura a la JVM a través de la verificación del código de tiempo de comstackción, por lo que va más allá al introducir restricciones de inmutabilidad a sus clases, de modo que los elementos de la colección no puedan mutarse mientras exista la colección.

Esto puede o no ser lo que quieres lograr: si estás justo después de usar las colecciones de Clojure en la JVM, iría con el enfoque de Cornelius, de lo contrario, si estás interesado en seguir un enfoque de progtwigción pura dentro de Java, entonces podrías dar Pure4J un bash.

Divulgación: soy el desarrollador de este

La respuesta más votado sugiere utilizar directamente las colecciones de Clojure, que creo que es una muy buena idea. Desafortunadamente, el hecho de que clojure sea un lenguaje de tipado dynamic y Java no hace que las bibliotecas de clojure sean muy incómodas de usar en Java.

Debido a esto y la falta de envoltorios livianos y fáciles de usar para los tipos de colecciones clojure, he escrito mi propia biblioteca de envoltorios Java usando generics para los tipos de colecciones clojure con un enfoque en la facilidad de uso y la claridad cuando se trata a las interfaces.

https://github.com/cornim/ClojureCollections

Tal vez esto sea útil para alguien.

PD: por el momento solo se han implementado PersistentVector, PersistentMap y PersistentList.

totallylazy es una muy buena biblioteca FP que tiene implementaciones de:

  • PersistentList : las implementaciones concretas son LinkedList y TreeList (para acceso aleatorio)
  • PersistentMap : las implementaciones concretas son HashTreeMap y ListMap
  • PersistentSortedMap
  • PersistentSet : la implementación concreta es TreeSet

Ejemplo de uso:

 import static com.googlecode.totallylazy.collections.PersistentList.constructors.*; import com.googlecode.totallylazy.collections.PersistentList; import com.googlecode.totallylazy.numbers.Numbers; ... PersistentList list = list(1, 2, 3); // Create a new list with 0 prepended list = list.cons(0); // Prints 0::1::2::3 System.out.println(list); // Do some actions on this list (eg remove all even numbers) list = list.filter(Numbers.odd); // Prints 1::3 System.out.println(list); 

totallylazy se mantiene constantemente. La principal desventaja es la ausencia total de Javadoc.

https://github.com/arnohaase/a-foundation es otro puerto de las bibliotecas de Scala.

También está disponible en Maven Central: com.ajjpj.a-foundation: a-foundation

Me sorprende que nadie haya mencionado a Vavr. Lo uso desde hace mucho tiempo.

http://www.vavr.io

Descripción de su sitio:

Vavr core es una biblioteca funcional para Java. Ayuda a reducir la cantidad de código y boost la solidez. Un primer paso hacia la progtwigción funcional es comenzar a pensar en valores inmutables. Vavr proporciona colecciones inmutables y las funciones y estructuras de control necesarias para operar con estos valores. Los resultados son hermosos y solo funcionan.