【问题标题】:java: very large trees?java: 很大的树?
【发布时间】:2011-11-08 10:55:31
【问题描述】:

目标是建造非常大的树。 我所说的非常大是指数亿个节点,大约只有几 GB。

问题在于通用数据结构的开销太大。我买不起“节点”对象和子“地图”。我需要以非常紧凑的方式将其直接编码到内存中。

因此,我想知道是否存在一些以整数作为键和值的树的内存高效实现,而不在内部使用对象,因此需要(键的 4 字节 + 值的 4 字节 + 子索引的 4 字节 + 一些可用散列空间的字节数 = 每个条目平均 15 个字节),这将允许我使用外部映射 intkeys 和 intvalues 来搜索树。

有人吗?

PS:在内部使用对象至少使用 5 倍以上的空间:8 个引用 + 4 个额外的哈希空间 + 16 个对象头 + 8 个键引用 + 8 个值引用 + 8 个父引用 + 8 个子引用 + (16 + x) for children map obj = 每个条目将近 76+x 个字节。 (例如,我们的默认实现每个条目需要大约 100 个字节)

【问题讨论】:

  • 在节点中保存对对象的引用会更有效率吗?这是否不需要为每个持有的项目提供一个额外的指针内存,以及一个额外的取消引用循环才能到达该项目?我在这里错过了什么吗?
  • 我的意思是根本不在内部使用对象。内部只有一个(或几个)巨大的 int[] 数组。
  • 是否必须使用Java? (否)SQL 数据库会是更好的选择吗?
  • 每个节点有多少个子节点?除非该索引是指向实际子索引列表的指针,否则如何避免子索引每个节点只有四个字节?
  • @GregS:正如你所说,它是一个指针。一旦树有超过 2^32 / 16 = 2.38 亿个节点,8 个字节可能是必要的,或者更容易处理。

标签: java data-structures tree large-data


【解决方案1】:

这实际上不是 Java 特定的问题,而是一个一般概念。

试试这个:http://webdocs.cs.ualberta.ca/~holte/T26/tree-as-array.html

关键是使用基元数组,以避免对象开销。

【讨论】:

  • 那是二叉树(最好是平衡树!)。完整树的解决方案要复杂得多。
  • @arnaud 这只是一个提示,因为您没有提供关于树结构本身的太多信息。您这样评论您的问题:I meant not using objects internally at all. Solely one (or a few) huge int[] arrays internally. 这基本上就是我的建议。
  • 是的,谢谢你的好意。只是由于“完整”解决方案的绝对复杂性,我不愿意自己实现......它真的很复杂......数组的每个部分都将代表孩子的迷你哈希表,当它增长,它必须移动到别处,然后必须回收空间,必须更新父级的指针等等……与它相比,平衡二叉树到数组是小菜一碟。
  • @arnaud - 那你为什么说“目标是建造非常大的树”?描述你真正想要完成的事情,你可能会得到更好的解决方案。但我仍然不会期待任何开箱即用的东西。
  • @parsifal:嗯?! ...因为目标是“建造非常大的树”。 (作为旁注,这里的响应指的是平衡二叉树,它绝不适合普通树。有效地实现后者比文章中描述的单纯双射要复杂得多)
【解决方案2】:

我不知道有什么具体的树实现可以做到这一点,但 VTD-XML 在内部使用带有指向缓冲区的指针的令牌数组表示 XML 树(DOM)。也许您可以从他们的解决方案中得到启发?

【讨论】:

    【解决方案3】:

    你可能会发现这个库可以帮助你实现你想要的——它是专门为将值存储为原语而设计的,并在幕后进行了一些字节码黑客操作,以产生存储对象的错觉。在...时使用它

    ...您希望在内存中有效地存储大量数据。该库可以显着减少 Full GC 时间并减少内存消耗。

    它没有特定的 Tree 集合,但它可以解决问题,具体取决于您的需要。

    http://code.google.com/p/vanilla-java/wiki/HugeCollections

    【讨论】:

      【解决方案4】:

      我认为您不会找到已经为您实现的任何东西,但是您所描述的可以使用IntBuffer 很容易地实现。您将创建一个“包装器”类,将索引转换为缓冲区中的记录,并根据需要实例化/丢弃这些包装器(即,当您遍历树时,您可能不在乎关于持有对父级的引用)。

      有几个问题:

      • 包装器实例的垃圾收集:只要它们是短暂的,它们就永远不会离开 Eden,因此 GC 几乎是免费的。
      • 缓冲区内记录的垃圾收集:如果您有一个只写一次的树,这没有问题。否则,您需要维护一个空闲列表。不难,但需要一些时间。
      • 实现树的一般机制:这已经通过像TreeMap 这样的类为您完成。但算法非常简单,可从Wikipedia 获得。

      【讨论】:

        【解决方案5】:

        每个节点都可以引用其父节点,而不是保留子节点列表。因此序列化一个节点不需要超过三个整数值(父、键、值)。

        这种方法的一个问题是树遍历。获得所有节点子节点的明确列表将需要遍历所有节点。如果三元组按其父值排序,则可能会改进遍历。添加一个整数值,即指向下一个键的指针将允许将节点保留在链表中,从而简化节点的插入和删除工作。

        【讨论】:

        • 您需要能够搜索树。对于给定的键列表,您需要恢复该值。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-09-25
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多