3

根据API,这些是事实:

  • seek(long bytePosition)方法简单地说,将指针移动到bytePosition参数指定的位置。
  • bytePosition大于文件长度时,除非在(新)端写入一个字节,否则文件长度不会改变。
  • 如果跳过的长度中存在数据,则此类数据保持不变。

但是,我很好奇的情况是:当有一个没有数据的文件(0字节)并且我执行以下代码时:

file.seek(100000-1);
file.write(0);

0所有 100,000 字节几乎立即被填满。我可以在 10 毫秒内时钟超过 200GB。

但是,当我尝试使用其他方法(例如BufferedOutputStream相同的过程)写入 100000 个字节时,几乎需要无限长的时间。

造成这种时间差异的原因是什么?有没有更有效的方法来创建一个n字节文件并用 s 填充它0

编辑: 如果数据没有实际写入,文件是如何填充数据的?示例此代码:

RandomAccessFile out=new RandomAccessFile("D:/out","rw");
out.seek(100000-1);
out.write(0);
out.close();

这是输出:

输出

另外,如果文件足够大,由于空间不足,我无法再写入磁盘。

4

2 回答 2

7

当您将 100,000 个字节写入 aBufferedOutputStream时,您的程序会显式访问文件的每个字节并写入零。

当您RandomAccessFile.seek()在本地文件上使用 a 时,您是在间接使用 C 系统调用fseek()。如何处理取决于操作系统。

在大多数现代操作系统中,都支持稀疏文件。这意味着如果您请求一个 100,000 字节的空文件,则实际上并没有使用 100,000 字节的磁盘空间。当您写入字节 100,001 时,操作系统仍然不使用 100,001 字节的磁盘。它为包含“真实”数据的块分配少量空间,并单独跟踪空白空间。

当您读取稀疏文件时,例如,通过fseek()读取字节 50,000,然后读取,操作系统可以说“好的,我没有为字节 50,000 分配磁盘空间,因为我注意到字节 0 到 100,000 是空的。因此我可以返回0这个字节。”。这对调用者是不可见的。

这具有节省磁盘空间和提高速度的双重目的。您已经注意到速度的提高。

更一般地说,fseek()直接转到文件中的某个位置,因此它是 O(1) 而不是 O(n)。如果将文件与数组进行比较,就像做x = arr[n]而不是for(i = 0; i<=n; i++) { x = arr[i]; }

这个描述以及维基百科上的描述可能足以理解为什么寻找 100,000 个字节然后写入比写入 100,000 个零更快。但是,您可以阅读 Linux 内核源代码以了解稀疏文件是如何实现的,您可以阅读RandomAccessFileJDK 中的源代码和 JRE 源代码,以了解它们如何交互。但是,这可能比您需要的更详细。

于 2017-02-23T17:10:02.827 回答
2

您的操作系统和文件系统支持稀疏文件,在这种情况下,将实现seek以利用此功能。

这与 Java 并没有真正的关系,它只是 C 库的一个特性fseekfwrite函数,它们很可能是您正在使用的 JRE 上文件实现背后的后端。

更多信息:https ://en.wikipedia.org/wiki/Sparse_file

有没有更有效的方法来创建一个 n 字节的文件并用 0 填充它?

在支持它的操作系统上,您可以将文件截断为所需的大小,而不是发出write调用。但是,这似乎在 Java API 中不可用。

于 2017-02-23T17:02:09.673 回答