使用緩衝區 (NIO 二)

Buffer屬性

從概念上講,緩衝區Buffer是對原始數據元素的數組的包裝。緩衝區 Buffer有四個重要的屬性

  1. 容量(Capacity):緩衝區可以容納的最大數據元素數。 容量是在創建緩衝區時設置的,無法更改。
  2. 限制(Limit):不應讀取或寫入的緩衝區的第一個元素。 換句話說,緩衝區中活動元素的數量。
  3. 位置(Position):下一個要讀取或寫入的元素的索引。 該位置由在調用的get()和put()方法會自動更新。
  4. 標記(Mark):標記的位置。 調用mark()方法,記住mark = position。 調用reset()方法,重新將讓position = mark 。 mark初始值爲爲-1

這四個屬性之間的以下關係始終成立:

0 <= 標記 <= 位置 <= 限制 <= 容量 
0 <= mark <= position <= limit <= capacity

下圖是新創建的容量爲10的ByteBuffer的邏輯視圖。position設置爲0,capacity和limit 設置爲10。 mark最初是未定義的,默認爲-1。

在這裏插入圖片描述

創建Buffer

例如,分配一個可容納100個字符的CharBuffer

CharBuffer charBuffer = CharBuffer.allocate (100);

或者使用自己提供的數組用作緩衝區的存儲

char [] myArray = new char [100];
CharBuffer charbuffer = CharBuffer.wrap (myArray);

創建一個帶有位置position和偏移量offset的緩衝區

char [] myArray = new char [100];
CharBuffer charbuffer = CharBuffer.wrap (myArray , 12, 42);

上述定義之後,Buffer屬性爲

  • mark = -1
  • position = 12
  • limit = 54
  • capacity = 100

測試代碼

public class CreateBuffers2 {
	public static void main(String[] args) {
		char [] myArray = new char [100];
		CharBuffer charBuffer = CharBuffer.wrap (myArray , 12, 42);
		System.out.println(charBuffer.capacity());
		System.out.println(charBuffer.limit());
		System.out.println(charBuffer.position());
	}
}

測試結果

100
54
12

訪問Buffer

爲了訪問NIO中的緩衝區,每個緩衝區類都提供get()和put()方法。 抽象基類有如下方法

public abstract class ByteBuffer extends Buffer implements Comparable
{
        // This is a partial API listing
        public abstract byte get();
        public abstract byte get (int index);
        public abstract ByteBuffer put (byte b);
        public abstract ByteBuffer put (int index, byte b);
}

填充和輸出Buffer

public class FillingBuffers2 {
	public static void main(String[] args) {
		CharBuffer charBuffer = CharBuffer.allocate (100);
		// 填充
		charBuffer.put("Hello").put('o');
		charBuffer.put(0, 'M').put('w');
		charBuffer.flip();
		System.out.println(charBuffer);
	}
}

填充結果

Melloow

簡單分析一下執行過程,顯示put相關的邏輯
在這裏插入圖片描述
然後執行

charBuffer.flip();

內部執行邏輯爲

	public final Buffer flip() {
        limit = position;
        position = 0;
        mark = -1;
        return this;
    }

等價於

charBuffer.limit(charBuffer.position()).position(0);

在這裏插入圖片描述

能夠輸出值得區間爲[position,limit)

幾個訪問方法的比較
除了訪問位置、限制、容量值的方法以及做標記和重置的方法外,此類還定義了以下可對緩衝區進行的操作:

  • clear() 使緩衝區爲一系列新的通道讀取或相對放置 操作做好準備:它將限制設置爲容量大小,將位置設置爲 0。
	 public final Buffer clear() {
        position = 0;
        limit = capacity;
        mark = -1;
        return this;
    }
  • flip() 使緩衝區爲一系列新的通道寫入或相對獲取 操作做好準備:它將限制設置爲當前位置,然後將位置設置爲 0。
    public final Buffer flip() {
        limit = position;
        position = 0;
        mark = -1;
        return this;
    }
  • rewind() 使緩衝區爲重新讀取已包含的數據做好準備:它使限制保持不變,將位置設置爲 0。
	public final Buffer rewind() {
        position = 0;
        mark = -1;
        return this;
    }

爲了幫忙大家理解上述方法,我使用以上三種不同的方式重複讀取10次Buffer內容

  • flip()方式
public class ReadTenTimesBuffer {
	public static void main(String[] args) {
		CharBuffer charBuffer = CharBuffer.allocate (100);
		charBuffer.put("i'm a buffer");
		
		for (int i = 0; i < 10; i++) {
			charBuffer.flip();
			while (charBuffer.hasRemaining()) {
				System.out.print(charBuffer.get());
			}
			System.out.println();
		}
	}
}
  • rewind()方式
public class ReadTenTimesBuffer {
	public static void main(String[] args) {
		CharBuffer charBuffer = CharBuffer.allocate (100);
		charBuffer.put("i'm a buffer");
		
		charBuffer.limit(charBuffer.position()).position(0);
		for (int i = 0; i < 10; i++) {
			while (charBuffer.hasRemaining()) {
				System.out.print(charBuffer.get());
			}
			System.out.println();
			charBuffer.rewind();
		}
	}
}
  • clear()方式
public class ReadTenTimesBuffer {
	public static void main(String[] args) {
		CharBuffer charBuffer = CharBuffer.allocate (100);
		charBuffer.put("i'm a buffer");
		
		int position = charBuffer.position();
		charBuffer.limit(position).position(0);
		for (int i = 0; i < 10; i++) {
			while (charBuffer.hasRemaining()) {
				System.out.print(charBuffer.get());
			}
			System.out.println();
			charBuffer.clear().limit(position);
		}
	}
}

綜合例子,文件拷貝

使用FileChannel和間接緩衝區複製文件

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
 
public class FileCopyUsingFileChannelAndBuffer 
{
    public static void main(String[] args) 
    {
        String inFileStr = "screen.png";
        String outFileStr = "screen-out.png";
        long startTime, elapsedTime; 
        int bufferSizeKB = 4;
        int bufferSize = bufferSizeKB * 1024;
 
        // Check file length
        File fileIn = new File(inFileStr);
        System.out.println("File size is " + fileIn.length() + " bytes");
        System.out.println("Buffer size is " + bufferSizeKB + " KB");
        System.out.println("Using FileChannel with an indirect ByteBuffer of " + bufferSizeKB + " KB");
         
        try (   FileChannel in = new FileInputStream(inFileStr).getChannel();
                FileChannel out = new FileOutputStream(outFileStr).getChannel() ) 
        {
            // Allocate an indirect ByteBuffer
            ByteBuffer bytebuf = ByteBuffer.allocate(bufferSize);
 
            startTime = System.nanoTime();
             
            int bytesCount = 0;
            // Read data from file into ByteBuffer
            while ((bytesCount = in.read(bytebuf)) > 0) { 
                // flip the buffer which set the limit to current position, and position to 0.
                bytebuf.flip();
                out.write(bytebuf); // Write data from ByteBuffer to file
                bytebuf.clear(); // For the next read
            }
             
            elapsedTime = System.nanoTime() - startTime;
            System.out.println("Elapsed Time is " + (elapsedTime / 1000000.0) + " msec");
        } 
        catch (IOException ex) {
            ex.printStackTrace();
        }
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章