Java 一种节省空间的数据结构,用于存储和查找大量(均匀分布的)整数

Java 一种节省空间的数据结构,用于存储和查找大量(均匀分布的)整数,java,memory-management,data-structures,hashcode,Java,Memory Management,Data Structures,Hashcode,我需要在内存中保存并查找一百万个均匀分布的整数。 我的工作量非常需要查找。 我当前的实现使用HashSet(Java)。我看到了良好的查找性能,但内存使用情况并不理想(几十MB)。 您能想出一种更有效的(内存)数据结构吗? 编辑:解决方案将需要支持对数据结构的少量添加 背景: 上述整数问题是以下问题的简化: 我有一组一百万个字符串(我的“字典”),我想知道字典是否包含给定的字符串。 这本字典太大了,无法放入内存,所以我愿意牺牲一点点准确性来减少内存占用。我将切换到包含每个字符串的Hashcode

我需要在内存中保存并查找一百万个均匀分布的整数。 我的工作量非常需要查找。
我当前的实现使用HashSet(Java)。我看到了良好的查找性能,但内存使用情况并不理想(几十MB)。
您能想出一种更有效的(内存)数据结构吗?
编辑:解决方案将需要支持对数据结构的少量添加

背景:
上述整数问题是以下问题的简化:
我有一组一百万个字符串(我的“字典”),我想知道字典是否包含给定的字符串。

这本字典太大了,无法放入内存,所以我愿意牺牲一点点准确性来减少内存占用。我将切换到包含每个字符串的Hashcode值(整数)的字典,而不是实际的字符。我假设每个字符串发生冲突的概率只有
1M/2^32

听起来你可以只保留一个排序的
int[]
,然后进行二进制搜索。如果有一百万个值,那就需要20次比较才能得到任何值-这足够快吗?

有一些
IntHashSet
原语实现可用


快速的谷歌搜索吸引了我。还有一个apache[开源]的实现。我更喜欢apache实现,尽管它有一些开销[它被实现为]

您可能想看一看,在Lucene中使用的实现甚至比标准Java实现更快,因为它忽略了一些标准边界检查

虽然Jon Skeet的答案为一项小投资提供了很好的储蓄,但我认为你可以做得更好。由于您的数字分布相当均匀,因此可以使用插值搜索来更快地查找(大致为O(logn)而不是O(logn))。对于100万个项目,您可能计划进行4次比较,而不是20次

如果您想再多做一点工作,将内存(大致)减半,您可以将其构建为一个两级查找表,基本上是trie的一种简单版本

您可以将(大概)32位整数分解为两个16位片段。您将使用前16位作为查找表第一级的索引。在这个级别上,您将有65536个指针,对于整数的这一部分,每个可能的16位值对应一个指针。这会把你带到桌子的第二层。对于这一部分,我们将在所选指针和上一个指针之间进行二进制或插值搜索——即,第二级中的所有值在前16位具有相同的值

但是,当我们查看第二个表时,我们已经知道值的16位——因此,我们不需要存储值的所有32位,而只需要存储值的其他16位

这意味着第二级不再占用4兆字节,而是将其减少到2兆字节。除此之外,我们还需要第一级表,但它只有65536x4=256K字节

这几乎肯定会提高对整个数据集进行二进制搜索的速度。在最坏的情况下(对第二级使用二进制搜索),我们可以进行多达17次比较(1+log265536)。但是平均值会比这个更好——因为我们只有一百万个项目,在每一个第二级“分区”中平均只能有1 000 000/65536=~15个项目,给出大约1+log2(16)=5个比较。在第二级使用插值搜索可能会进一步减少这一点,但当您仅从5个比较开始时,您就没有多少空间进行真正的显著改进。如果第二级的搜索平均只有15个条目,那么你使用的搜索类型不会有太大区别——即使是线性搜索也会非常快


当然,如果您愿意,您可以进一步使用4级表(整数中的每个字节对应一个)。然而,这可能是一个值得商榷的问题,这是否会为你省下足够多的钱,值得你为此烦恼。至少马上,我的直接猜测是,你会做相当多的额外工作,以获得相当小的节省(仅仅存储百万整数的最后一个字节显然就占用了1兆字节,而导致这一结果的三个级别的表显然占用了相当多的空间,因此您可以将级别的数量增加一倍,以节省大约半兆字节的空间。如果您的情况是只需多保存一点就可以产生很大的差异,那么尝试一下——但是因此,我怀疑这种回报是否能证明额外投资的合理性。

如果你愿意接受一个小的误报机会,以换取内存使用的大幅减少,那么a可能正是你所需要的

Bloom筛选器由k个哈希函数和一个n位的表组成,最初为空。若要向表中添加项,请将其馈送到k个哈希函数中的每一个(获得一个介于0和n之间的数字)−1) 并设置相应的位。若要检查表中是否有项,请将其馈送到每个k哈希函数,并查看是否设置了所有相应的k位

误报率为1%的Bloom过滤器每项大约需要10位;随着每项添加更多位,误报率会迅速降低


我认为您可能会重新考虑原始问题(使用有效的单词列表),而不是尝试优化“优化”

我建议查看根树/Trie

基本上,您存储的是一种以字符串为前缀的树,每当字典中有选择时就进行分支。它有一些有趣的副作用(允许非常有效地过滤前缀),可以为具有较长常用前缀的字符串节省一些内存,而且速度相当快

一些示例实现