笔记:I/O流-内存映射文件
内存映射文件时利用虚拟内存实现来将一个文件或者文件的一部分映射到内存中,然后整个文件就可以当作数组一样的访问,这个比传统的文件操作要快得多,Java 使用内存映射文件首先需要从文件中获取一个channel(通道),通道时磁盘文件的一个抽象,他使得我们可以访问诸如内存映射、文件加锁机制以及文件间快速数据传递等操作系统特性,然后通过调用 FileChannel 类的 map 方法从这个通道获取一个映射块(ByteBuffer),然后就可以读取/写入文件块内容,map 支持的模式有如下:
- FileChannel.MapMode.READ_ONLY:所产生的缓冲区是只读的,如果对只读的缓冲区进行写操作,将会导致 ReadonlyBufferException 异常
- FileChannel.MapMode.READ_WRITE:所产生的缓冲区是可写的,任何修改都会在某个时刻写回文件(写入不是及时的)
- FileChannel.MapMode.PRIVATE:所产生的缓冲区是可写的,但是任何修改对这个缓冲区来说都是私有的,不会传播到文件中
映射文件示例代码:
- 读取文件:
Path filePath = Paths.get("D:\\我的游戏\\World of Warcraft\\Data\\data", "data.001");
long startTime = System.currentTimeMillis(); //获取开始时间
try {
try (FileChannel channel = FileChannel.open(filePath, StandardOpenOption.READ)) {
int size = 40960;
long readByteSize = 0;
ByteBuffer byteBuffer = ByteBuffer.allocate(size);
int readCount;
do {
readCount = channel.read(byteBuffer);
byte[] readBytes = new byte[byteBuffer.limit()];
// channel.read 后其 position 是 readCount,因此需要读取数据时,需要重置 position = 0
byteBuffer.position(0);
for(int i=0;i< readBytes.length;i++){
readBytes[i] = byteBuffer.get();
}
byteBuffer.clear();
if (readCount != -1) {
readByteSize += readCount;
}
System.out.print("readCount +" + readCount + " readByteSize " + readByteSize + " ");
} while (readCount != -1);
}
} catch (IOException ex) {
ex.printStackTrace();
}
long endTime = System.currentTimeMillis(); //获取结束时间
System.out.println("程序运行时间: " + (endTime - startTime) + "ms");
- 写入文件(使用 RandomAccessFile 和 MappedByteBuffer):
Random random = new Random();
Path writeFilePath = Paths.get("d:\\channel.dat");
startTime = System.currentTimeMillis(); //获取开始时间
try {
byte[] buff = new byte[40960];
long position = 0;
long size = buff.length;
RandomAccessFile randomAccessFile = new RandomAccessFile(writeFilePath.toString(), "rw");
try (FileChannel outChannel = randomAccessFile.getChannel()) {
for (int i = 0; i < 1000; i++) {
random.nextBytes(buff);
MappedByteBuffer buffer = outChannel.map(FileChannel.MapMode.READ_WRITE, position, size);
buffer.put(buff);
position += size;
}
}
} catch (IOException ex) {
ex.printStackTrace();
}
endTime = System.currentTimeMillis(); //获取结束时间
System.out.println("程序运行时间: " + (endTime - startTime) + "ms");