【问题标题】:Android memory efficient collection for keeping tokenized strings用于保存标记化字符串的 Android 内存高效集合
【发布时间】:2012-11-20 03:37:32
【问题描述】:

在我的项目中,我试图从包含字符串标记的资产文件夹中加载一个 600KB 的文件。

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

我从 HashSet 开始 - 但它会将字符串数据炸毁到 10MB - 导致内存不足问题

然后,切换到 ArrayList - 但这也增加到 6MB。

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

所以,我主要关心的仍然是这些数据:

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

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

【问题讨论】:

  • 大小问题与 Java 字符串而不是集合有关

标签: java android collections


【解决方案1】:

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

假设字符串中的字符是ASCII,那么把它们转成String对象马上就会加倍大小(byte -> char),然后你需要为每个字符串增加32字节的开销。然后,如果将字符串放入 HashSet 中,则集合中的每个条目大约需要额外的 32 个字节。

使用ArrayList<String>,每个条目的开销为 4 个字节,但现在查找是 O(N) ... 或 O(logN),如果您保持列表有序并使用二进制搜索。无论哪种方式,您的内存预算都超出了您的预算。

为了不超出预算,您将不得不使用针对内存使用优化的自定义哈希表数据结构将您的字符数据作为单个字节数组保存在内存中。 p>

这是一个假设的实现。

  1. 分配一个int[] 为哈希数组。大小应该是一个素数,大约是令牌数量的一半到五分之一。
  2. 分配一个足够大的byte[] 来保存令牌文件。
  3. 对于哈希数组中的每个槽:
    • 逐字节扫描文件,查找哈希码映射到插槽的所有令牌,
    • 将每个标记复制到字节数组中,并在其后加上一个终止符,
    • 如果找到任何标记,请将第一个标记开头的字节数组偏移量写入哈希数组槽...否则将其设置为-1
  4. 进行查找:
    • 将测试字符串转换为字节,
    • 散列测试字符串的字节(使用与上述相同的散列算法),并将其映射到散列槽,
    • 从哈希槽中的偏移量开始,将测试字符串的字节与byte[] 中的字节进行比较。重复直到找到匹配项,或者到达 next 哈希数组元素中的偏移量。

如您所见,填写byte[] 的过程涉及多次扫描输入文件。然而,这可以提前完成,然后可以更新输入文件以包含所需顺序的字节。

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

上述设计的主要缺点是:

  • 创建数据结构的成本很高
  • 无法以节省空间或时间的方式更新数据结构
  • 如果您对集合进行迭代,则必须创建一堆 String 对象来表示条目...或公开字节数组和偏移量。

【讨论】:

    【解决方案2】:

    这是一个有趣的问题!我通常使用 util 包中的 HashMap 类进行存储,例如这样。你的问题可能不太适合安卓设备的内存空间,所以我会建议一个替代方案。

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

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

    【讨论】:

    • 我可以尝试将所有标记存储在一个 char[] 中,并用say "," 作为分隔符,但是我可以在 O(1) 处搜索一个单词——任何库或 Algo/您可以建议的数据结构。
    • 如果你要在数组中创建一个索引来告诉你分隔符在哪里是可能的。否则 O(n) 在 n 大小的数组中找到分隔符。
    • 我认为您可以尝试的另一个选项是使用 HashMap。为令牌使用适当的密钥,然后您只需要在需要时查找它。 HashMap 非常快,但在内存空间方面不如数组有效。但是看到您可以将每个标记存储为单独的实体,您可以跳过存储分隔符(除非它们很重要)。如果使用此方法内存不足,则可以将 HashMap 用作缓存,如果在其中找不到某些内容,则从磁盘中检索它并将其存储在映射中。您必须确保偶尔删除地图中的内容。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2010-10-18
    • 1970-01-01
    • 1970-01-01
    • 2013-05-28
    • 1970-01-01
    • 1970-01-01
    • 2019-11-12
    相关资源
    最近更新 更多