用于保持标记化字符串的Android内存高效集合

时间:2012-12-02 14:33:14

标签: java android collections

在我的项目中,我正在尝试从包含字符串标记的assets文件夹中加载600KB文件。

我需要这些令牌在o(1)或任何固定时间可用/搜索/包含。

我开始使用HashSet - 但它将字符串数据炸成10MB - 导致内存不足问题

然后,切换到ArrayList - 但这也吹到了6MB。

我尝试使用原始String,但是当我从StringBuffer构建它时,append方法的固有问题就出现了 - 导致内存不足问题。

所以,我的主要担忧仍然是这些数据:

  • 最初的600KB - 所以集合应该保持在1或2MB
  • 之内
  • 查找最好在O(1)
  • 之内

是否有任何可以帮助我的好的Java集合(甚至来自任何其他库)?

2 个答案:

答案 0 :(得分:0)

在内存中以1到2Mb 表示这些令牌以及支持O(1)查找将非常困难。没有任何标准集合类型能够为您执行此操作, 我也不知道任何第三方Java库。 (S-Space项目有一个TrieSet实现,但我查看了代码,我很确定它不会满足您的空间或性能要求......)

假设字符串中的字符是ASCII,那么将它们转换为String对象会立即使大小翻倍(byte - > char),然后您需要添加32字节的开销每个字符串。然后,如果将字符串放入HashSet,则集合中的每个条目都需要大约32个额外字节。

ArrayList<String>每个条目的开销是4个字节,但如果保持列表的顺序并使用二进制搜索,则查找现在是O(N) ...或O(logN)。无论哪种方式,你仍然超出你的记忆预算。

要保持在预算之下,您将不得不使用针对内存使用而优化的自定义哈希表数据结构将内存中的字符数据保存为单个字节数组。

这是一个假设的实施。

  1. int[]分配为哈希数组。大小应该是一个素数,大约是令牌数量的一半到五分之一。
  2. 分配一个足够大的byte[]来存放令牌文件。
  3. 对于哈希数组中的每个插槽:
    • 按字节扫描文件,查找其哈希码映射到插槽的所有令牌,
    • 将每个标记复制到字节数组,并使用终结符字节
    • 跟随它
    • 如果找到任何令牌,请将第一个令牌开头的字节数组偏移量写入散列数组插槽...否则将其设置为-1
  4. 要进行查找:
    • 将测试字符串转换为字节,
    • 哈希测试字符串的字节(使用与上面相同的哈希算法),并将其映射到哈希槽,
    • 从散列槽中的偏移量开始,将测试字符串的字节与byte[]中的字节进行比较。重复直到获得匹配,或者到达 next 哈希数组元素中的偏移量。
  5. 如您所见,填充byte[]的过程涉及多次扫描输入文件。但是,这可以事先完成,然后可以更新输入文件以包含所需顺序的字节。

    空间使用量是每字节字符串数据一个字节+每个字符串1个字节开销+主要哈希数组中每个插槽的4个字节(+杂项O(1)开销)。查找平均为O(1),但常量取决于哈希数组大小。 (越大越好。)

    上述设计的主要缺点是:

    • 创建数据结构很昂贵
    • 无法以空间或时间有效的方式更新数据结构
    • 如果迭代该集合,则必须创建一组String对象来表示条目...或者公开字节数组和偏移量。

答案 1 :(得分:0)

这是一个有趣的问题!我通常在util包中使用HashMap类来存储这样的东西。你的问题可能不容易适应Android设备的内存空间,所以我会建议一个替代方案。

对于存储设备Android设备通常使用固态硬盘,通常相当快,所以为什么不将磁盘上的大部分数据留在资源文件夹中直到需要?您可以构造一个类来缓存最常用的结果,修改数据也应该是合理的。如果这不适合,也许您可​​以使用android SDK中提供的数据管理工具,例如sqlite,它将为您完成一些艰苦的工作。

如果你可以避免使用通常更好的选择。操作字符串可能非常昂贵。如果您使用其他数据类型(甚至是字符或字节数组),您可能会发现代码更复杂但在内存方面更有效。