NIO入門java
前段時間在公司裏處理一些大的數據,並對其進行分詞、提取關鍵字等。雖然說任務基本完成了(效果也不是特別好),對於Java還沒入門的我來講前先後後花了2周的時間,我本身也是醉了。固然也有涉及到機器學習的知識,我想陸陸續續的記錄下個人這一次任務的過程,也算作一個總結。編程
首先,手上有這麼個達G級別的文件,按照Java普通I/O的方式確定是不行的了,劃分文件的話,也不知何年何月才能讀完。因此後來上網查找了相關資料,才知道有這麼個神奇的NIO。數組
在Java編程中,I/O是用流的方式讀取文件,全部I/O都被視爲單個的字節的移動,經過一個稱爲Stream的對象一次移動一個字節。Java中新的輸入/輸出(NIO)庫是在JDK1.4中引入的。NIO彌補了原來I/O的不足,它在標準Java代碼中提供了高速、面向塊的I/O。經過定義包含數據的塊,以及經過以塊的形式來處理這些數據,NIO不用使用本機代碼就能夠利用低級優化,這是原來的I/O包所沒法作到的。機器學習
流與塊的比較學習
原來的I/O庫和NIO最重要的區別就是數據打包和傳輸的方式,原來的I/O以流的方式處理數據,而NIO以塊的方式處理數據。優化
面向流的I/O系統一次一個字節的處理數據,一個輸入流產生一個字節的數據,一個輸出流產生一個字節的數據。spa
一個面向塊的I/O系統以塊的形式處理數據。每個操做都在一步中產生或者消費一個數據塊。按塊處理數據比按字節處理數據要快得多,即使它沒有面向流的I/O那樣的簡單性。code
通道和緩衝區對象
通道和緩衝區是NIO中的核心對象,幾乎在每個I/O操做中都要使用它們。blog
通道是對原I/O包中的流的模擬。到任何目的地或來自任何地方的全部數據都必須經過一個Channel對象。一個Buffer實質上是一個容器對象。發送給一個通道的全部對象都必須首先存放到緩衝區中;一樣的,從通道中讀取任何的數據都必須首先讀取到緩衝區裏。
什麼是緩衝區?
Buffer是一個對象,它包含一些要寫入或者剛讀出的數據。 在 NIO 中加入Buffer對象,體現了新庫與原 I/O 的一個重要區別。在面向流的 I/O 中,您將數據直接寫入或者將數據直接讀到Stream對象中。在 NIO 庫中,全部數據都是用緩衝區處理的。在讀取數據時,它是直接讀到緩衝區中的。在寫入數據時,它是寫入到緩衝區中的。任什麼時候候訪問 NIO 中的數據,您都是將它放到緩衝區中。緩衝區實質上是一個數組。一般它是一個字節數組,可是也可使用其餘種類的數組。可是一個緩衝區不只僅是一個數組。緩衝區提供了對數據的結構化訪問,並且還能夠跟蹤系統的讀/寫進程。
緩衝區類型
最經常使用的緩衝區類型是ByteBuffer。一個ByteBuffer能夠在其底層字節數組上進行 get/set 操做(即字節的獲取和設置)。ByteBuffer不是 NIO 中惟一的緩衝區類型。事實上,對於每一種基本 Java 類型都有一種緩衝區類型:
ByteBuffer
CharBuffer
ShortBuffer
IntBuffer
LongBuffer
FloatBuffer
DoubleBuffer
每個Buffer類都是Buffer接口的一個實例。 除了ByteBuffer,每個 Buffer 類都有徹底同樣的操做,只是它們所處理的數據類型不同。由於大多數標準 I/O 操做都使用ByteBuffer,因此它具備全部共享的緩衝區操做以及一些特有的操做。
下面看一下FloatBuffer的簡單例子:
1 import java.io.FileInputStream; 2 import java.io.FileNotFoundException; 3 import java.io.FileOutputStream; 4 import java.nio.ByteBuffer; 5 import java.nio.FloatBuffer; 6 import java.nio.channels.FileChannel; 7 8 // UseFloatBuffer 9 10 public class UseFloatBuffer { 11 12 public static void main(String[] args) throws Exception { 13 14 FloatBuffer fb=FloatBuffer.allocate(10); 15 for (int i=0; i<fb.capacity(); i++) { 16 float f=(float)((float)i/10*(2*Math.PI)); 17 fb.put(f); 18 } 19 fb.flip(); 20 while (fb.hasRemaining()){ 21 float f=fb.get(); 22 System.out.println(f); 23 } 24 } 25 }
什麼是通道?
Channel是一個對象,能夠經過它讀取和寫入數據。拿 NIO 與原來的 I/O 作個比較,通道就像是流。正如前面提到的,全部數據都經過 Buffer 對象來處理。您永遠不會將字節直接寫入通道中,相反,您是將數據寫入包含一個或者多個字節的緩衝區。一樣,您不會直接從通道中讀取字節,而是將數據從通道讀入緩衝區,再從緩衝區獲取這個字節。簡而言之,就是NIO的大體流程爲:輸入文件->緩衝區->通道->緩衝區->程序處理數據->緩衝區->通道->緩衝區->輸出文件;I/O的大體流程爲:輸入文件->流->程序處理數據->流->輸出文件。
通道類型
通道與流的不一樣之處在於通道是雙向的。而流只是在一個方向上移動(一個流必須是InputStream或者OutputStream的子類),而通道能夠用於讀、寫或者同時用於讀寫。
實踐起來:NIO 中的讀和寫
讀和寫是 I/O 的基本過程。從一個通道中讀取很簡單:只需建立一個緩衝區,而後讓通道將數據讀到這個緩衝區中;寫入也至關簡單:建立一個緩衝區,用數據填充它,而後讓通道用這些數據來執行寫入操做。
從文件中讀取
若是使用原來的 I/O,那麼咱們只需建立一個FileInputStream並從它那裏讀取。而在 NIO 中,狀況稍有不一樣:咱們首先從FileInputStream獲取一個Channel對象,而後使用這個通道來讀取數據。
在 NIO 系統中,任什麼時候候執行一個讀操做,您都是從通道中讀取,可是您不是直接從通道讀取。由於全部數據最終都駐留在緩衝區中,因此您是經過通道讀到緩衝區中的數據。
所以讀取文件涉及三個步驟:
(1) 從FileInputStream獲取Channel
(2) 建立Buffer
(3) 將數據從Channel讀到Buffer中。
1 FileInputStream fin=new FileInputStream("read.txt"); 2 FileChannel fc=fin.getChannel(); 3 ByteBuffer buffer=ByteBuffer.allocate(1024); 4 fc.read(buffer);
寫入文件
在 NIO 中寫入文件相似於從文件中讀取。首先從FileOutputStream獲取一個通道;下一步是建立一個緩衝區並在其中放入一些數據 - 在這裏,數據將從一個名爲data的數組中取出,最後一步是寫入緩衝區中。
1 FileOutputStream fout=new FileOutputStream("write.txt"); 2 FileChannel fc=fout.getChannel(); 3 ByteBuffer buffer=ByteBuffer.allocate(1024); 4 for (int i=0; i<data.length; i++) { 5 buffer.put(data[i]); 6 } 7 buffer.flip(); 8 fc.write(buffer);
實戰練習
咱們以一個名爲 CopyFile.java 的簡單程序做爲這個練習的基礎,它將一個文件的全部內容拷貝到另外一個文件中。CopyFile.java 執行三個基本操做:首先建立一個Buffer,而後從源文件中將數據讀到這個緩衝區中,而後將緩衝區寫入目標文件。這個程序不斷重複 ― 讀、寫、讀、寫 ― 直到源文件結束。
1 // CopyFile 2 3 import java.io.*; 4 import java.nio.*; 5 import java.nio.channels.*; 6 7 public class CopyFile { 8 9 static public void main( String args[] ) throws Exception { 10 String infile="E:\\北京歡迎你.txt"; 11 String outfile="E:\\out.txt"; 12 13 FileInputStream fin=new FileInputStream(infile); 14 FileOutputStream fout=new FileOutputStream(outfile); 15 FileChannel fcin = fin.getChannel(); 16 FileChannel fcout = fout.getChannel(); 17 18 ByteBuffer buffer = ByteBuffer.allocate(1024); 19 20 while (true) { 21 buffer.clear(); 22 int r=fcin.read(buffer); 23 if (r == -1) { 24 break; 25 } 26 buffer.flip(); 27 fcout.write(buffer); 28 } 29 } 30 }