筆記: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");
? ?
筆記:I/O流-內存映射文件