Buffer屬性
從概念上講,緩衝區Buffer是對原始數據元素的數組的包裝。緩衝區 Buffer有四個重要的屬性
- 容量(Capacity):緩衝區可以容納的最大數據元素數。 容量是在創建緩衝區時設置的,無法更改。
- 限制(Limit):不應讀取或寫入的緩衝區的第一個元素。 換句話說,緩衝區中活動元素的數量。
- 位置(Position):下一個要讀取或寫入的元素的索引。 該位置由在調用的get()和put()方法會自動更新。
- 標記(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();
}
}
}