Java NIO入門


NIO入門

前段時間在公司里處理一些大的數據,並對其進行分詞、提取關鍵字等。雖說任務基本完成了(效果也不是特別好),對於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系統一次一個字節的處理數據,一個輸入流產生一個字節的數據,一個輸出流產生一個字節的數據。

一個面向塊的I/O系統以塊的形式處理數據。每一個操作都在一步中產生或者消費一個數據塊。按塊處理數據比按字節處理數據要快得多,即便它沒有面向流的I/O那樣的簡單性。

通道和緩沖區

通道和緩沖區是NIO中的核心對象,幾乎在每一個I/O操作中都要使用它們。

通道是對原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 }

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM