更有效地存储(字符串,整数)元组并应用二进制搜索

时间:2016-09-11 19:42:08

标签: java optimization storage binaryfiles binary-search

简介

我们将元组(string,int)存储在二进制文件中。字符串代表一个单词(没有空格或数字)。为了找到一个单词,我们应用二进制搜索算法,因为我们知道所有元组都是根据单词排序的。

为了存储它,我们使用writeUTF作为字符串,writeInt作为整数。除此之外,我们假设现在没有办法区分元组的开头和结尾,除非事先知道它们。

问题

  

当我们应用二进制搜索时,我们在文件中得到一个位置(即(a + b)/ 2),我们可以使用Random Access File中的方法读取该位置,即我们可以读取该位置的字节。但是,由于我们可能处于这个词的中间,我们无法知道这些词的开头或结尾。

解决方案

以下是我们提出的两种可能的解决方案,但是,我们正在尝试确定哪一种更节省空间/更快。

  • 方法1:我们考虑将其存储为字符串(使用例如。writeChars或{{3因为在这种情况下,我们可以在元组的末尾插入一个空字符。也就是说,我们可以确定用于序列化数据的方法都不会使用空字符,因为我们存储的信息(数字和数字)具有更高的ASCII值表示。

  • 方法2:我们保持相同的结构,但我们将每个元组与6-8(或更少)字节的随机噪声分开(整个文件中相同)。在这种情况下,我们假设单词具有较低的熵,因此它们不太可能有任何随机性的迹象。即使integer可能得到与随机噪声中的4个字节完全相同的4个字节,随后的两个字节也不会(很有可能)。

您会推荐以下哪种方法?有没有更好的方法来存储这种信息。注意,我们不能序列化整个文件,然后将其反序列化到内存中,因为它非常大(我们不允许)。

2 个答案:

答案 0 :(得分:2)

我认为你正试图优化速度和速度。空间(按此顺序)。

我使用不同的布局,由2个文件构建:

  1. 整数+索引文件
    每个“记录”正好是8个字节长,低4个是记录的整数值,而高4个字节是一个整数,表示另一个文件中记录的偏移量(字符文件)。
  2. 字符文件
    连续的字符文件(UTF-8编码或您选择的任何内容)。 “记录”未分开,不以任何方式终止,简单的1 x 1字符。例如,记录GoodHelloMorning将显示为GoodHelloMorning
  3. 要迭代数据集,可以直接访问整数/索引文件(recordNum * 8是记录的字节偏移量),读取整数和字符偏移量,再加上下一条记录的字符偏移量(这是recordNum * 8 + 12处的4字节整数,然后从您从索引文件中读取的偏移量之间的字符文件中读取字符串。完成!

答案 1 :(得分:0)

  

它不到200MB。一个单词最多20个字符。

为什么要这么麻烦?除非你在一些受到严格限制的系统上工作,否则将所有内容加载到Map<String, Integer>并加速几个数量级。

但是,让我们说,我忽略了一些东西,让我们继续。

  

方法1:我们不是将整数存储为数字,而是将其存储为字符串(使用例如writeChars或writeUTF),因为在这种情况下,我们可以插入一个空字符

你不必像你说的那样说你的话没有数字。因此,您始终可以唯一地解析0124some456word789之类的内容。

效率取决于分布。您可以赢得4倍(单位数字)或丢失2.5倍(10位数字)。你可以通过使用更高的基数来节省一些东西。但是有字符串的存储空间,它可能占主导地位。

  

方法2:我们保持相同的结构,但我们将每个元组与6-8(或更少)字节的随机噪声分开(整个文件中相同)。

这太浪费了。在数据字节之间使用四个零可以:

  • 找到至少四个零的序列。
  • 找到最后的零。
  • 这是最后一个分隔符字节。

方法3:使用一些黑客,您可以确保该数字不包含零字节(假设它不使用整个范围或用五个字节表示它)。然后就会有一个零字节。

方法4:由于磁盘是按块组织的,因此您应该将数据拆分为4个KiB块。然后,您可以添加一些时间标题,以便您快速访问数据(开始索引第8,16等数据)。应该顺序扫描例如第8和第16块之间的范围,因为它比二进制搜索更简单,更快。