緩沖區基礎
抽象類Buffer是java.nio包支持緩沖區的基礎。 Buffer 的工作方式就象內存中用于讀寫基本數據類型的 RandomAccessFile 。象 RandomAccessFile 一樣,使用 Buffer ,所執行的下一個操作(讀/寫)在當前某個位置發生。執行讀/寫操作中的任一個都會改變那個位置,所以在寫操作之后進行讀操作不會讀到剛才所寫的內容,而會讀到剛才所寫內容之后的數據。 Buffer 提供了四個指示方法,用于訪問線性結構(從最高值到最低值):
capacity() :表明緩沖區的容量大小, 一旦確定了大小, 將不能再改變;limit() :告訴您到目前為止已經往緩沖區填了多少字節,或者讓您用 :limit(int newLimit) 來改變這個限制 position() :告訴您當前的位置,以執行下一個讀/寫操作 mark() :為了稍后用 reset() 進行重新設置而記住某個位置 flip() :交換限制指針和位置指針,然后將位置置為 0,并廢棄已經做的mark標記緩沖區的基本操作是讀 get() 和寫 put() ;然而,這些方法在子類中都是針對每種數據類型的特定方法。為了說明這一情況,讓我們研究一個簡單示例,該示例演示了從同一個緩沖區讀和寫一個字符。在清單 1 中, flip() 方法交換限制和位置,然后將位置置為 0,并廢棄標記,讓您讀剛才所寫的數據:
清單 1. 讀/寫示例import java.nio.*;...CharBuffer buff = ...;buff.put('A');buff.flip();char c = buff.get();System.out.println("An A: " + c);
現在讓我們研究一些具體的 Buffer 子類。
緩沖區類型
Merlin 具有 7 種特定的 Buffer 類型,每種類型對應著一個基本數據類型(不包括 boolean):
ByteBuffer //存放任何除boolean類型外的其他基本類型CharBuffer //存放charDoubleBuffer //存放doubleFloatBuffer //存放floatIntBuffer //存放intLongBuffer //存放longShortBuffer //存放short在本文后面,我將討論第 8 種類型 MappedByteBuffer ,它用于內存映射文件。如果您必須使用的類型不是這些基本類型,則可以先從 ByteBuffer 獲得字節類型,然后將其轉換成 Object 或其它任何類型。 創建緩沖區一共有兩種類型的緩沖區,直接緩沖區和非直接緩沖區。在創建緩沖區時,可以要求創建直接緩沖區,創建直接緩沖區的成本要比創建間接緩沖區高,但這可以使運行時環境直接在該緩沖區上進行較快的本機 I/O 操作。因為創建直接緩沖區所增加的成本,所以直接緩沖區只用于長生存期的緩沖區,而不用于短生存期、一次性且用完就丟棄的緩沖區。而且,只能在 ByteBuffer 這個級別上創建直接緩沖區,如果希望使用其它類型,則必須將 Buffer 轉換成更具體的類型。判斷一個緩沖區是否是直接緩沖區,可以調用isDirect()方法。有三種方式來獲取一個緩沖區的對象:a. 調用allocate()或者allocateDirect()方法直接分配,其中allocateDirect()返回的是直接緩沖區。b. 包裝一個數組,如: byte[] b = new byte[1024]; ByteBuffer bb = ByteBuffer.wrap(b);c. 內存映射,即調用FileChannel的map()方法。緩沖區基本屬性這幾個屬性是每個緩沖區都有的并且是常用的操作。a. 容量(capacity),緩沖區大小b. 限制(limit),第一個不應被讀取或寫入的字節的索引,總是小于容量。c. 位置(position),下一個被讀取或寫入的字節的索引,總是小于限制。d. clear()方法:設置limit為capacity,position為0。e. filp()方法:設置limit為當前position,然后設置position為0。f. rewind()方法:保持limit不變,設置position為0。緩沖區數據操作操作包括了讀取和寫入數據兩種。讀取數據使用get()及其系列方法,除boolean外,每一種類型包括了對應的get()方法,如getInt(),getChar()等,get()方法用來讀取字節,支持相對和絕對索引兩種方式。寫入數據使用put()及其系列方法,和get()方法是對應的。package nio;import java.io.FileInputStream;import java.io.FileOutputStream;import java.nio.ByteBuffer;import java.nio.channels.FileChannel;public class BufferDemo ...{ public static void main(String[] args) throws Exception...{ //分配一個非直接緩沖區 ByteBuffer bb = ByteBuffer.allocate(100); //向緩沖區寫入0到100的字節制 for(int i = 0; i <100; i++)...{ byte b = (byte) (Math.random() * 100); bb.put(b); } System.out.println("寫入文件前的緩沖區數據"); bb.flip(); while(bb.hasRemaining()) System.out.print(bb.get() + " "); System.out.println(); //獲取一個關聯到文件buffer.txt的信道 FileChannel fc = new FileOutputStream("buffer.txt").getChannel(); //將緩沖區數據寫到文件中 bb.flip(); fc.write(bb); //防止緩存 fc.force(true); //關閉信道 fc.close(); bb = null; fc = null; //下面從文件中讀取數據 fc = new FileInputStream("buffer.txt").getChannel(); ByteBuffer bb2 = ByteBuffer.allocate((int) fc.size()); fc.read(bb2); System.out.println("從文件讀取的緩沖區數據"); bb2.flip(); while(bb2.hasRemaining()) System.out.print(bb2.get() + " "); System.out.println(); fc.close(); bb2 = null; fc = null; }}內存映射文件
第 8 種 Buffer 類型 MappedByteBuffer 只是一種特殊的 ByteBuffer 。 MappedByteBuffer 將文件所在區域直接映射到內存。通常,該區域包含整個文件,但也可以只映射部分文件。所以,必須指定要映射文件的哪部分。而且,與其它 Buffer 對象一樣,這里沒有構造函數;必須讓 java.nio.channels.FileChannel 的 map() 方法來獲取 MappedByteBuffer 。此外,無需過多涉及通道就可以用 getChannel() 方法從 FileInputStream 或 FileOutputStream 獲取 FileChannel 。通過從命令行傳入文件名來讀取文本文件的內容,清單 4 顯示了 MappedByteBuffer :
清單 4. 讀取內存映射文本文件import java.io.*;import java.nio.*;import java.nio.channels.*;import java.nio.charset.*;public class ReadFileBuff { public static void main(String args[]) throws IOException { if (args.length != 0) { String filename = args[0]; FileInputStream fis = new FileInputStream(filename); FileChannel channel = fis.getChannel(); int length = (int)channel.size(); MappedByteBuffer byteBuffer = channel.map(FileChannel.MapMode.READ_ONLY, 0, length); Charset charset = Charset.forName("ISO-8859-1"); CharsetDecoder decoder = charset.newDecoder(); CharBuffer charBuffer = decoder.decode(byteBuffer); for (int i=0, n=charBuffer.length(); i<n; i++) { System.out.print(charBuffer.get()); } } }}
Powered by: BlogJava Copyright © 比特鼠