【问题标题】:what's a good persistent collections framework for use in java?什么是在 java 中使用的好的持久性集合框架?
【发布时间】:2012-01-24 10:23:15
【问题描述】:

我所说的持久性集合是指 clojure 中的集合。

例如,我有一个包含元素 (a,b,c) 的列表。 对于普通列表,如果我添加 d,我的原始列表将 (a,b,c,d) 作为其元素。 使用持久列表,当我调用 list.add(d) 时,我会返回一个新列表,其中包含 (a,b,c,d)。 但是,该实现尝试尽可能在列表之间共享元素,因此它比简单地返回原始列表的副本更节省内存。 它还具有不可变的优点(如果我持有对原始列表的引用,那么它将始终返回原始的 3 个元素)。

这一切都在其他地方得到了更好的解释(例如http://en.wikipedia.org/wiki/Persistent_data_structure)。

无论如何,我的问题是......提供此功能以供在 java 中使用的最佳库是什么?我可以以某种方式使用 clojure 集合(除了直接使用 clojure)吗?

【问题讨论】:

  • 你的意思是类似于 Java 的 LinkedList?我现在明白你的意思了,看看functionaljava.org,它可能对你有帮助。
  • 你知道functionjava.org中的副手实现了我上面提到的那种结构吗? (我知道有点厚脸皮,否则我去看看源代码)
  • 是我之前的评论 - 是的,但如果可能的话,我宁愿使用 Clojure 的(因为我知道它们是久经沙场的)

标签: java collections clojure functional-programming


【解决方案1】:

直接使用Clojure 中的即可。虽然显然您可能不想使用它本身的语言,但您仍然可以直接使用持久性集合,因为它们都只是 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");

(免责声明:我实际上并没有编译该代码:)

缺点是集合没有类型,即它们没有泛型。

【讨论】:

  • 缺少泛型将是一个问题,唉,我会调查的。否则,这是一个非常好的建议,尤其是在这些被广泛使用的情况下。
  • 断言map2 != map 实际上失败了。如果可能的话,似乎有一些内部优化可以重用现有地图。
  • 另外,它是.valAt() 而不是.get()
  • 这对我有用:IPersistentMap map = PersistentHashMap.create("key1", "value1");断言 map.valAt("key1").equals("value1"); IPersistentMap map2 = map.assoc("key1", "value2");断言地图2!=地图;断言 map2.valAt("key1").equals("value2");
【解决方案2】:

pcollections 呢?

您还可以查看Clojure 的持久性集合实现(例如PersistentHashMap)。

【讨论】:

  • 是的,我偶然发现了 pcollections。有谁知道它们是否在生产系统中被大量使用(这是一个工作项目)?也许这应该是一个单独的问题。
  • 不知道为什么我没有想到直接使用 Clojure 的集合,总体而言这可能是一个更好的主意。
  • 使用 Clojure 的唯一问题是将整个 jar 放入您的项目中(除了泛型,但您可以使用一些包装类来隐藏它)。这对你来说可能是也可能不是问题。我自己从未使用过 PCollections,如果您必须绝对依赖可靠的实现,请使用 Clojure(检查许可证和/或与开发人员交谈,因为您可能只能提取所需的实现)。
  • totallylazy 看起来比 Clojure 好得多,因为它不仅有持久集合的实现,而且还有大量方便的方法来处理这些集合。除了 Clojure 集合只能在 Clojure 中使用之外,Clojure 是一种动态语言(不能在 Java 中使用)。
【解决方案3】:

我一直在寻找一个苗条的、Java“友好”的持久性收集框架,并把这个线程中提到的 TotallyLazyPCollections 用于测试,因为它们听起来对我来说最有希望。

两者都提供了合理的简单接口来操作持久列表:

// TotallyLazy
PersistentList<String> original = PersistentList.constructors.empty(String.class);
PersistentList<String> modified = original.append("Mars").append("Raider").delete("Raider");

// PCollections
PVector<String> original = TreePVector.<String>empty();
PVector<String> modified = original.plus("Mars").plus("Raider").minus("Raider");

PersistentListPVector 都扩展了 java.util.List,因此这两个库都应该很好地集成到现有环境中。

然而,TotallyLazy 在处理较大的列表时会遇到性能问题(正如@levantpied 上面的评论中已经提到的)。在我的 MacBook Pro(2013 年末)上,插入 100.000 个元素并返回不可变列表需要 TotallyLazy ~2000ms,而 PCollections 在 ~120ms 内完成。

我的(简单)测试用例可在Bitbucket 上找到,如果有人想更深入地了解一下。

[更新]:我最近看了Cyclops X,这是一个针对函数式编程的高性能且更完整的库。 Cyclops 还包含一个用于持久集合的模块。

【讨论】:

    【解决方案4】:

    https://github.com/andrewoma/dexx 是 Scala 的持久性集合到 Java 的一个端口。它包括:

    • Set、SortedSet、Map、SortedMap 和 Vector
    • 适配器将持久性集合视为 java.util 等效项
    • 帮助轻松构建

    【讨论】:

      【解决方案5】:

      Paguro provides type-safe versions of the actual Clojure collections 用于 Java 8+。包括:List(向量)、HashMap、TreeMap、HashSet、TreeSet。它们的行为与您在问题中指定的方式完全相同,并且一直是 painstakingly fit into the existing java.util collections interfaces 以实现最大的类型安全 Java 兼容性。他们也是a little faster than PCollections

      在 Paguro 中编写您的示例如下所示:

      // List with the elements (a,b,c)
      ImList<T> 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<T> newList = list.append(d);
      
      list.size(); // still returns 3
      
      newList.size(); // returns 4
      

      你说,

      实现尝试在列表之间共享元素 只要有可能,它的内存效率和速度都比 只需返回原始列表的副本。它还具有 不可变的优点(如果我持有对原始文件的引用 列表,那么它总是会返回原来的 3 个元素)。

      是的,这正是它的行为方式。 Daniel Spiewak explains the speed and efficiency of these collections 比我做得好很多。

      【讨论】:

        【解决方案6】:

        可能想查看clj-ds。我没用过,但看起来很有希望。根据项目自述文件,它从 Clojure 1.2.0 中提取了数据结构。

        【讨论】:

        • 它似乎解决了直接使用 Clojure 集合产生的所有问题。我会试一试。谢谢。
        【解决方案7】:

        Functional Java 实现了持久化 List、惰性 List、Set、Map 和 Tree。可能还有其他的,但我只是根据网站首页上的信息。

        我也很想知道最好的 Java 持久数据结构库是什么。我的注意力转向了函数式 Java,因为它在书中提到,Functional Programming for Java Developers

        【讨论】:

          【解决方案8】:

          您可以使用pcollections(持久集合)库:

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

          【讨论】:

            【解决方案9】:

            投票最多的答案建议直接使用 clojure 集合,我认为这是一个非常好的主意。不幸的是,clojure 是一种动态类型语言而 Java 不是,这使得 clojure 库在 Java 中使用起来非常不舒服。

            由于这个原因以及 clojure 集合类型缺乏轻量级、易于使用的包装器,我编写了自己的 Java 包装器库,使用 clojure 集合类型的泛型,重点是易用性和清晰性说到接口。

            https://github.com/cornim/ClojureCollections

            也许这对某人有用。

            P.S.:目前只实现了 PersistentVector、PersistentMap 和 PersistentList。

            【讨论】:

            • 这里的贡献非常好!它是一个加号
            【解决方案10】:

            与 Cornelius Mund 一样,Pure4J 将 Clojure 集合移植到 Java 并添加泛型支持。

            但是,Pure4J 旨在通过编译时代码检查将纯编程语义引入 JVM,因此它进一步为您的类引入了不变性约束,以便在集合存在时不能改变集合的元素。

            这可能是也可能不是您想要实现的目标:如果您刚刚在 JVM 上使用 Clojure 集合,我会选择 Cornelius 的方法,否则,如果您有兴趣在 Java 中追求纯编程方法,那么你可以试试 Pure4J。

            披露:我是这个的开发者

            【讨论】:

              【解决方案11】:

              totallylazy 是一个非常好的 FP 库,它具有以下实现:

              • PersistentList&lt;T&gt;:具体实现为LinkedList&lt;T&gt;TreeList&lt;T&gt;(用于随机访问)
              • PersistentMap&lt;K, V&gt;:具体实现为HashTreeMap&lt;K, V&gt;ListMap&lt;K, V&gt;
              • PersistentSortedMap&lt;K, V&gt;
              • PersistentSet&lt;T&gt;:具体实现为TreeSet&lt;T&gt;

              使用示例:

              import static com.googlecode.totallylazy.collections.PersistentList.constructors.*;
              import com.googlecode.totallylazy.collections.PersistentList;
              import com.googlecode.totallylazy.numbers.Numbers;
              
              ...
              
              PersistentList<Integer> 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 (e.g. remove all even numbers)
              list = list.filter(Numbers.odd);
              // Prints 1::3
              System.out.println(list);
              

              totallylazy 一直在维护。主要缺点是完全没有 Javadoc。

              【讨论】:

              • 另一个问题是它很慢。例如,this 需要大约 700-800 毫秒才能运行。使用clj-ds 的类似事情,即this,所需的时间减少了约 9 倍(大约 85 毫秒)。相比之下,Java 的 HashMap 大约需要 25 毫秒。
              • @levantpied 您的example 中的num 有多大?
              • @StefanHaberl final int num = 100000;
              【解决方案12】:

              我很惊讶没有人提到 vavr。我已经用了很长时间了。

              http://www.vavr.io

              来自他们网站的描述:

              Vavr 核心是 Java 的函数库。它有助于减少代码量并增加健壮性。函数式编程的第一步是开始思考不可变的值。 Vavr 提供了不可变的集合以及对这些值进行操作所需的函数和控制结构。结果很漂亮,而且很有效。

              【讨论】:

                【解决方案13】:

                https://github.com/arnohaase/a-foundation 是 Scala 库的另一个端口。

                它也可以从 Maven Central 获得:com.ajjpj.a-foundation:a-foundation

                【讨论】:

                  猜你喜欢
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 2015-02-15
                  • 1970-01-01
                  • 2011-07-03
                  • 1970-01-01
                  相关资源
                  最近更新 更多