Java中ByteBuffer與ByteArray之間的轉換
1. 概述
本教學將闡述如何在 Java 中實作ByteBuffer和Byte數組之間的轉換。理解這兩種格式之間的轉換對於文件 I/O 處理和網路通訊等任務至關重要。
我們將研究將ByteBuffer轉換為Byte[]以及將 Byte[] 轉換為 ByteBuffer 的不同方法,然後討論何時使用每種方法。
2. 將ByteBuffer轉換為Byte組數組
在Java中, ByteBuffer是java.nio套件中的一個核心類別。它的引入是為了有效率地處理二進位資料。因此,它被廣泛應用於I/O操作。
首先,讓我們先從探討如何從ByteBuffer中提取Byte數組開始。
2.1. 使用ByteBuffer.array()
最簡單直接的方法是使用array()方法。通常,此方法傳回支援緩衝區的位元組數組:
@Test
void whenUsingArrayMethod_thenConvertToByteArray() {
byte[] givenBytes = {1, 6, 3};
ByteBuffer buffer = ByteBuffer.wrap(givenBytes);
byte[] bytes = buffer.array();
assertArrayEquals(givenBytes, bytes);
}
這裡需要特別注意的是, array()方法僅在緩衝區具有可存取的底層數組時才有效。否則,它會拋出UnsupportedOperationException :
@Test
void givenBufferWithoutBackingArray_whenCallingArray_thenThrowUnsupportedOperationException() {
ByteBuffer buffer = ByteBuffer.allocateDirect(4);
assertThrows(UnsupportedOperationException.class, buffer::array);
}
**在呼叫 ` array()之前,最好先使用hasArray()方法檢查緩衝區是否有支援數組**。這樣做可以防止出現UnsupportedOperationException.
另請注意,如果緩衝區是唯讀的,則array()方法會拋出ReadOnlyBufferException :
@Test
void givenReadOnlyBuffer_whenCallingArray_thenThrowReadOnlyBufferException() {
ByteBuffer buffer = ByteBuffer.wrap(new byte[] {1, 2, 3});
ByteBuffer readOnlyBuffer = buffer.asReadOnlyBuffer();
assertThrows(ReadOnlyBufferException.class, readOnlyBuffer::array);
}
正如我們所看到的, array()方法拋出了ReadOnlyBufferException異常。
2.2. 使用ByteBuffer.get()
或者,我們可以使用get()方法。它提供了一種更強大、更靈活的方法來提取Byte數組:
@Test
void whenUsingGetMethod_thenConvertToByteArray() {
byte[] givenBytes = {5, 4, 2};
ByteBuffer buffer = ByteBuffer.wrap(givenBytes);
byte[] bytes = new byte[buffer.remaining()];
buffer.get(bytes);
assertArrayEquals(givenBytes, bytes);
}
此方法會建立緩衝區資料的新副本,從而確保緩衝區與傳回的位元組數組之間的獨立性。
請注意,我們可以指定偏移量和長度,以便更精確地控制從緩衝區複製資料的方式:
@Test
void givenByteBuffer_whenUsingGetWithParamsMethod_thenConvertToByteArray() {
byte[] givenBytes = {5, 4, 2, 7};
ByteBuffer buffer = ByteBuffer.wrap(givenBytes);
byte[] bytes = new byte[2];
buffer.get(bytes, 0, 2);
assertArrayEquals(new byte[] {5, 4}, bytes);
}
這裡,目標數組只包含 2 個元素,與複製的位元組數相符。
3. 將Byte組數組轉換為ByteBuffer
現在,讓我們深入了解如何從位元組數組建立ByteBuffer 。
3.1. 使用ByteBuffer.wrap()
最常用的方法是使用wrap()方法。顧名思義,它將給定的位元組數組包裝到一個緩衝區:
@Test
void whenUsingWrapMethod_thenConvertToByteBuffer() {
byte[] givenBytes = {1, 2, 3};
ByteBuffer buffer = ByteBuffer.wrap(givenBytes);
assertArrayEquals(givenBytes, buffer.array());
}
值得注意的是,這種方法會建立一個緩衝區,該緩衝區直接使用提供的陣列作為其底層儲存。因此,對緩衝區的任何變更都會反映在數組中,反之亦然。
ByteBuffer也提供了wrap(array, offset, length) 。但是,它不是從指定的偏移量開始建立一個新數組,而是將整個原始數組重新包裝,並將位置設為給定的偏移量:
@Test
void givenByteArray_whenUsingWrapWithParamsMethod_thenConvertToByteBuffer() {
byte[] givenBytes = {1, 2, 3, 7, 8};
ByteBuffer buffer = ByteBuffer.wrap(givenBytes, 1, 2);
byte[] actualBytes = new byte[buffer.remaining()];
buffer.get(actualBytes);
assertArrayEquals(new byte[] {2, 3}, actualBytes);
}
如上所示,我們使用get()傳回切片,因為wrap(array, offset, length)方法無法控制底層陣列。
3.2. 使用ByteBuffer.allocate()
類似地,我們可以使用allocate()方法將位元組數組轉換為ByteBuffer 。我們應該將其與put()結合使用:
@Test
void whenUsingAllocateAndPutMethods_thenConvertToByteBuffer() {
byte[] givenBytes = {1, 9, 7};
ByteBuffer buffer = ByteBuffer.allocate(givenBytes.length);
buffer.put(givenBytes);
assertArrayEquals(givenBytes, buffer.array());
}
簡而言之,這種方法會分配一個新的緩衝區並複製數據,從而在緩衝區和原始數組之間建立完全的獨立性。
但是,使用**put() .**寫入資料後,緩衝區位置會移到最後。由此,我們需要呼叫flip()來重置緩衝區:
@Test
void givenByteArray_whenUsingPutWithFlip_thenReadWithoutException() {
byte[] givenBytes = {3, 8};
ByteBuffer buffer = ByteBuffer.allocate(givenBytes.length);
buffer.put(givenBytes);
buffer.flip();
assertEquals(3, buffer.get());
}
否則,使用get()方法從緩衝區讀取資料可能會導致BufferUnderflowException異常:
@Test
void givenByteArray_whenUsingPutWithoutFlip_thenThrowBufferUnderflowException() {
byte[] givenBytes = {1, 5};
ByteBuffer buffer = ByteBuffer.allocate(givenBytes.length);
buffer.put(givenBytes);
assertThrows(BufferUnderflowException.class, buffer::get);
}
如預期的那樣,測試案例證實,如果我們不使用flip()來重置緩衝區位置get()方法會拋出BufferUnderflowException異常。
4. 結論
在本文中,我們探討了 Java 中ByteBuffer和位元組數組之間進行轉換的各種方法。
完整的源代碼可以在 GitHub 上找到。