RandomAccessFile.seek()方法是如何工作的?
RandomAccessFile.seek()方法是如何工作的?
根据API,以下是事实:\n
- \n
seek(long bytePosition)
方法简单来说,将指针移动到指定的位置,该位置由bytePosition
参数指定。- 当
bytePosition
大于文件长度时,文件长度不会改变,除非在(新的)末尾写入一个字节。 - 如果跳过的长度中存在数据,则该数据保持不变。
\n
\n
\n
\n然而,我感兴趣的情况是:当文件没有数据(0字节)且执行以下代码时:\n
file.seek(100000-1); file.write(0);
\n所有的100,000字节几乎瞬间都被填充为0
。我可以在10毫秒内读取超过200GB。\n但是,当我尝试使用其他方法(如BufferedOutputStream
)写入100,000字节时,同样的过程需要几乎无限长的时间。\n这个时间差的原因是什么?有没有更有效的方法来创建一个包含n
字节并填充为0
的文件?\n编辑:\n如果实际上并没有写入数据,那么文件是如何被填充的呢?\n看一下这段代码:\n
RandomAccessFile out=new RandomAccessFile("D:/out","rw"); out.seek(100000-1); out.write(0); out.close();
\n这是输出:\n\n另外,如果文件足够大,由于空间不足,我将无法继续向磁盘写入。
如何使用RandomAccessFile.seek()方法?
在操作系统和文件系统支持稀疏文件的情况下,seek方法会利用此功能实现。这与Java无关,它实际上是C库中fseek和fwrite函数的一种特性,这两个函数很可能是您所使用的JRE中File实现背后的后端。
如果操作系统支持,您可以使用truncate方法将文件截断为所需大小,而不是发出write调用。然而,这似乎在Java API中不可用。
稀疏文件必须是稀疏的。它必须通过seek操作创建。如果您向文件写入0或任何其他字节,则它就不是稀疏的。在文件中进行seek操作不会删除任何内容,它只是移动文件指针。
所以,如何实现seek方法呢?是否有一种方法可以查看内置库方法的实现呢?这适合作为另一个问题发布。
感谢您的答案。稀疏文件是否会覆盖已删除的数据呢?(数据已删除但尚未被覆盖)
以上就是关于RandomAccessFile.seek()方法的原因和解决方法的整理。
当您使用BufferedOutputStream
写入100,000个字节时,您的程序会显式地访问文件的每个字节并写入零。
当您在本地文件上使用RandomAccessFile.seek()
时,您间接使用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内核源代码,了解稀疏文件是如何实现的,您可以阅读JDK中的RandomAccessFile
源代码和JRE源代码,了解它们的交互。然而,这可能是您不需要的更多细节。
这个回答也解决了您的修改。数据都是0,这是由稀疏文件处理的。
感谢回答。
稀疏文件是否也会覆盖已删除的数据?(已删除但尚未覆盖的数据)
根据阅读此描述,我理解它不会,但是我进行了实验并发现我无法使用Piriform的Recuva恢复文件。(Windows 10)
如果您有其他问题,请将其作为一个新问题提问-但是您将需要解释您所指的“覆盖已删除的数据”和“已删除但尚未覆盖的数据”。此外,这可能属于不同的Stack Exchange站点,这取决于您询问的操作系统。