RandomAccessFile.seek() 是如何工作的?

How does RandomAccessFile.seek() work?

根据 API,事实如下:

不过,我比较好奇的情况是:当有一个没有数据(0字节)的文件时,我执行下面的代码:

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

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

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

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

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

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

这是输出:

此外,如果文件足够大,由于缺少 space,我无法再写入磁盘。

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

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

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

Is there a more efficient way to create a file of n bytes and fill it with 0s?

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

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

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

大多数现代操作系统都支持 sparse files。这意味着,如果您请求一个空的 100,000 字节文件,那么 100,000 字节的磁盘 space 并没有实际使用。当您写入字节 100,001 时,OS 仍然没有使用 100,001 字节的磁盘。它为包含[=36​​=]数据的块分配少量space,并单独跟踪空的space。

当你读取一个稀疏文件时,例如,通过fseek()ing到字节50,000,然后读取,OS可以说"OK, I have not allocated disk space for byte 50,000 because I have noted that bytes 0 to 100,000 are empty. Therefore I can return 0 for this byte.".这对调用者是不可见的。

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

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

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