在 1 到 2Mb 的内存中表示这些令牌并支持O(1)
查找将非常困难。没有任何标准集合类型能够为您做到这一点,而且我不知道有任何第三方 Java 库可以做到这一点。(S-Space项目有一个TrieSet
实现,但我查看了代码,我很确定它不会满足您的空间或性能要求......)
假设字符串中的字符是 ASCII,那么将它们转换为 String 对象会立即使大小翻倍(byte
-> char
),然后您需要为每个字符串添加 32 个字节的开销。然后,如果将字符串放入 aHashSet
中,则集合中的每个条目大约需要 32 个额外的字节。
每个条目的ArrayList<String>
开销为 4 个字节,但现在查找是O(N)
……或者O(logN)
如果您保持列表有序并使用二进制搜索。无论哪种方式,您仍然超出您的内存预算。
为了不超出您的预算,您将不得不使用针对内存使用进行了优化的自定义哈希表数据结构,并将您的字符数据作为单个字节数组保存在内存中。
这是一个假设的实现。
- 分配一个
int[]
作为哈希数组。大小应该是大约是令牌数量的一半到五分之一的素数。
- 分配一个
byte[]
足够大的空间来保存令牌文件。
- 对于哈希数组中的每个槽:
- 逐字节扫描文件以查找其哈希码映射到插槽的所有令牌,
- 将每个标记复制到字节数组并在其后跟一个终止符字节,
- 如果您找到任何标记,请将第一个标记开头的字节数组偏移量写入哈希数组槽......否则将其设置为
-1
.
- 要进行查找:
- 将测试字符串转换为字节,
- 散列测试字符串的字节(使用与上面相同的散列算法),并将其映射到散列槽,
- 从哈希槽中的偏移量开始,将测试字符串的字节与
byte[]
. 重复直到你得到一个匹配,或者你到达下一个哈希数组元素中的偏移量。
如您所见,填充过程byte[]
涉及多次扫描输入文件。然而,这可以事先完成,然后可以更新输入文件以包含所需顺序的字节。
空间使用量将是每个字符串数据字节一个字节 + 每个字符串 1 个字节开销 + 主哈希数组中每个插槽的 4 个字节(+ 各种O(1)
开销)。查找是O(1)
平均的,但常数取决于散列数组的大小。(越大越好。)
上述设计的主要缺点是:
- 创建数据结构是昂贵的
- 数据结构不能以空间或时间有效的方式更新
- 如果迭代集合,则必须创建一堆 String 对象来表示条目......或公开字节数组和偏移量。