HashMap替代内存高效的数据存储

时间:2010-10-19 19:47:34

标签: java collections guava

我目前有一个电子表格类型程序,它将数据保存在HashMaps的ArrayList中。当我告诉你这没有被证明是理想的时候,你无疑会感到震惊。开销似乎比数据本身多5倍的内存。

This question询问有效的馆藏库,答案是使用Google馆藏。 我的跟进是“哪个部分?。我一直在阅读文档,但不觉得它非常好地了解哪些类适合这个。 (我也对其他图书馆或建议持开放态度。)

所以我正在寻找能够以最小的内存开销存储密集的电子表格类型数据的东西。

  • 我的列目前由Field对象引用,行按索引引用,值为Objects,几乎总是字符串
  • 有些列会有很多重复值
  • 主要操作是根据某些字段的值更新或删除记录,以及添加/删除/组合列

我知道H2和Derby等选项,但在这种情况下,我不打算使用嵌入式数据库。

编辑:如果您建议使用图书馆,如果您能指出我在其中适用的特定课程,我也会感激不尽。虽然Sun的文档通常包含哪些操作是O(1)的信息,哪些是O(N)等,但我在第三方库中没有看到太多,也没有真正描述哪些类最适合什么类

10 个答案:

答案 0 :(得分:11)

  

有些专栏会有很多   重复值

立即向我建议可能使用FlyWeight pattern,无论您为收藏品选择何种解决方案。

答案 1 :(得分:5)

Trove集合应该特别注意占用的空间(我认为如果你坚持原始类型,他们也有定制的数据结构)..看看here

否则你可以试试Apache collections ..只需做你的基准测试!

无论如何,如果你有很多关于相同元素的引用,试着设计一些合适的模式(比如flyweight

答案 2 :(得分:4)

所以我假设您有Map<ColumnName,Column>的地图,其中的列实际上类似于ArrayList<Object>

一些可能性 -

  • 你完全确定内存是个问题吗?如果你只是普遍担心尺寸,那么值得确认这在运行程序中确实会成为一个问题。它需要大量的行和映射才能填满JVM。

  • 您可以使用集合中不同类型的地图测试数据集。根据您的数据,您还可以使用可能有用的预设尺寸/载荷系数组合初始化地图。我过去常常弄乱这个问题,如果你很幸运,你可能会减少30%的记忆。

  • 如何将数据存储在单个类似矩阵的数据结构(现有的库实现或类似列表列表的包装器之类)中,使用单个映射将列键映射到矩阵列?

答案 3 :(得分:3)

假设您的所有行都包含大多数相同的列,您可以为每一行使用一个数组,并使用Map&lt; ColumnKey,Integer&gt;查找哪些列指的是哪个单元格。这样,每个单元只有4-8个字节的开销。

如果经常重复使用字符串,则可以使用字符串池来减少字符串的重复。其他不可变类型的对象池可能有助于减少消耗的内存。

编辑:您可以将数据结构化为基于行或基于列。如果基于行(每行一个单元格数组)添加/删除行只是删除此行的问题。如果基于列,则每列可以有一个数组。这可以使处理原始类型更有效。也就是说,你可以有一个是int []的列,另一个是double [],对于整个列来说,它更常见的是具有相同的数据类型,而不是整行的数据类型相同。

但是,无论采用哪种方式来构建数据,都会优先选择行或列修改,并执行其他类型的添加/删除将导致重建整个数据集。

(我做的事情是有基于行的数据并在末尾添加列,假设行不够长,列有默认值,这可以避免在添加列时重建。而不是删除列,我有办法忽略它)

答案 4 :(得分:2)

Guava确实包含Table接口和基于哈希的实现。似乎很适合您的问题。请注意,这仍然标记为测试版。

答案 5 :(得分:1)

将其数据保存在HashMaps的ArrayList 中 嗯,这部分对我来说似乎非常低效。空HashMap已经分配16 * size of a pointer个字节(16代表默认初始容量),加上哈希对象的一些变量(14 + psize)。如果你有很多稀疏的行,这可能是个大问题。

一种选择是使用带有复合键的单个大散列(组合行和列)。虽然,这不会使整行操作非常有效。

此外,由于您未提及添加单元格的操作,因此您可以创建仅包含必要内部存储(initialCapacity参数)的哈希。

我对谷歌收藏品了解不多,所以无法帮到那里。此外,如果您发现任何有用的优化,请在此处发布!知道这会很有趣。

答案 6 :(得分:1)

我一直在尝试使用Colt项目中的SparseObjectMatrix2D。我的数据非常密集,但他们的Matrix类并没有真正提供任何扩大它们的方法,因此我选择了一个稀疏矩阵设置为最大尺寸。

对于相同的数据,似乎使用大约10%的内存并加载大约15%,并提供一些巧妙的操作方法。仍然对其他选项感兴趣。

答案 7 :(得分:1)

Chronicle Map每个条目的开销可能少于20个字节(请参阅a test证明这一点)。为了进行比较,java.util.HashMap的开销从37-42字节-XX:+UseCompressedOops到58-69字节不等,没有压缩oops(reference)。

此外,Chronicle Map在堆外存储键和值,因此它不存储Object头,这些头部不会被视为HashMap的开销。带有integrates的Chronicle Map Chronicle-Values,一个用于生成接口的flyweight实现的库,另一个答案中的模式suggested by Brian Agnew

答案 8 :(得分:0)

根据你的描述,似乎你不想使用HashMaps的ArrayList而是想要一个(链接)的ArrayList 的HashMap(每个ArrayList都是一列)。

我会从field-name到column-number添加一个双重映射,以及一些从不抛出IndexOutOfBoundsException的聪明的getter / setter。

您还可以使用ArrayList<ArrayList<Object>>(基本上是锯齿状的动态增长矩阵),并将映射保持为外部的字段(列)名称。

  

有些专栏会有很多   重复值

我怀疑这很重要,特别是如果它们是字符串,(它们是内化的),你的收藏品会存储对它们的引用。

答案 9 :(得分:0)

为什么不尝试使用像EHCache这样的缓存实现。 当我遇到同样的情况时,这对我来说非常有效 您可以将您的集合存储在EHcache实现中。 有如下配置:

Maximum bytes to be used from Local heap.

一旦您的应用程序使用的字节溢出了缓存中配置的字节,则缓存实现会负责将数据写入磁盘。您还可以使用最近最少使用的算法配置将对象写入磁盘的时间量。 使用这种类型的缓存实现,您可以确保避免任何内存不足错误。 它只会在很小程度上增加应用程序的IO操作 这只是配置的鸟瞰图。有许多配置可以优化您的需求。