繁体   English   中英

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

[英]HashMap alternatives for memory-efficient data storage

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

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

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

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

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

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

有些列会有很多重复的值

无论您为收藏品选择何种解决方案,我都会立即向我建议使用FlyWeight模式

Trove集合应特别关注占用的空间(我认为如果你坚持原始类型,它们也有定制的数据结构)。看看这里

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

在任何情况下,如果你有很多参考相同的元素尝试设计一些适合的模式(如flyweight

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

一些可能性 -

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

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

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

假设您的所有行都具有大多数相同的列,您可以只为每一行使用一个数组,并使用Map <ColumnKey,Integer>来查找哪些列引用哪个单元格。 这样,每个单元只有4-8个字节的开销。

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

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

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

(我做的事情是有基于行的数据并添加列到最后,假设一行不够长,列有一个默认值,这可以避免在添加列时重建。而不是删除列,我有一种忽视它的方法)

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

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

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

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

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

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

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

Chronicle Map每个条目的开销可能少于20个字节(参见测试证明这一点)。 为了进行比较,java.util.HashMap的开销从37-42字节到-XX:+UseCompressedOops到58-69字节不带压缩oops( 引用 )。

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

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

Maximum bytes to be used from Local heap.

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

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

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

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

有些列会有很多重复的值

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM