有如下情況下可以用到內存文件映射技術解決問題:
1.不要復制文件中所有的數據,只需要修改文件中局部的數據。
2.並行/分段處理大文件。
如下代碼示使用javaNIO局部修改文件中指定位置的部分數據:
/** * 修改文件中的某一部分的數據測試:將字定位置的字母改為大寫 * @param fName :要修改的文件名字 * @param start:起始字節 * @param len:要修改多少個字節 * @return :是否修改成功 * @throws Exception:文件讀寫中可能出的錯 * @author javaFound */
public static boolean changeFile(String fName,int start,int len) throws Exception{ //創建一個隨機讀寫文件對象
java.io.RandomAccessFile raf=new java.io.RandomAccessFile(fName,"rw"); long totalLen=raf.length(); System.out.println("文件總長字節是: "+totalLen); //打開一個文件通道
java.nio.channels.FileChannel channel=raf.getChannel(); //映射文件中的某一部分數據以讀寫模式到內存中
java.nio.MappedByteBuffer buffer= channel.map(FileChannel.MapMode.READ_WRITE, start, len); //示例修改字節
for(int i=0;i<len;i++){ byte src= buffer.get(i); buffer.put(i,(byte)(src-31));//修改Buffer中映射的字節的值
System.out.println("被改為大寫的原始字節是:"+src); } buffer.force();//強制輸出,在buffer中的改動生效到文件
buffer.clear(); channel.close(); raf.close(); return true; } //測試主方法
public static void main(String[] args) throws Exception{ changeFile("BigFileRW.java",3,5); System.out.println(" change OK... "); }
內存映射文件能讓你創建和修改那些因為太大而無法放入內存的文件。有了內存映射文件,你就可以認為文件已經全部讀進了內存,然后把它當成一個非常大的數組來訪問。這種解決辦法能大大簡化修改文件的代碼。
fileChannel.map(FileChannel.MapMode mode, long position, long size)將此通道的文件區域直接映射到內存中。注意,你必須指明,它是從文件的哪個位置開始映射的,映射的范圍又有多大;也就是說,它還可以映射一個大 文件的某個小片斷。
MappedByteBuffer 是ByteBuffer的子類,因此它具備了ByteBuffer的所有方法,但新添了force()將緩沖區的內容強制刷新到存儲設備中去、 load()將存儲設備中的數據加載到內存中、isLoaded()位置內存中的數據是否與存儲設置上同步。這里只簡單地演示了一下put()和 get()方法,除此之外,你還可以使用asCharBuffer( )之類的方法得到相應基本類型數據的緩沖視圖后,可以方便的讀寫基本類型數據。
import java.io.RandomAccessFile; import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; public class LargeMappedFiles { static int length = 0x8000000; // 128 Mb
public static void main(String[] args) throws Exception { // 為了以可讀可寫的方式打開文件,這里使用RandomAccessFile來創建文件。
FileChannel fc = new RandomAccessFile("test.dat", "rw").getChannel(); //注意,文件通道的可讀可寫要建立在文件流本身可讀寫的基礎之上
MappedByteBuffer out = fc.map(FileChannel.MapMode.READ_WRITE, 0, length); //寫128M的內容
for (int i = 0; i < length; i++) { out.put((byte) 'x'); } System.out.println("Finished writing"); //讀取文件中間6個字節內容
for (int i = length / 2; i < length / 2 + 6; i++) { System.out.print((char) out.get(i)); } fc.close(); } }
盡管映射寫似乎要用到FileOutputStream,但是映射文件中的所有輸出 必須使用RandomAccessFile,但如果只需要讀時可以使用FileInputStream,寫映射文件時一定要使用隨機訪問文件,可能寫時要讀的原因吧。
該程序創建了一個128Mb的文件,如果一次性讀到內存可能導致內存溢出,但 這里訪問好像只是一瞬間的事,這是因為,真正調入內存的只是其中的一小部分,其余部分則被放在交換文件上。這樣你就可以很方便地修改超大型的文件了(最大 可以到2 GB)。注意,Java是調用操作系統的"文件映射機制"來提升性能的。
