问题背景
在实际应用中,我们会碰的一个功能的输出的字节流,是另一个功能输入字节流的情况。一般情况下,我们可以使用ByteArrayOutputStream和ByteArrayInputStream实现相应的功能。
来看下面的测试代码:
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
public class ByteArrayTest {
public static void main(String[] argv) throws IOException {
ByteArrayOutputStream output = new ByteArrayOutputStream();
PrintWriter ps = new PrintWriter(output);
for (int i = 0; i < 1000; i++) {
ps.printf("This is line %d\n", i);
}
ps.close();
ByteArrayInputStream input = new ByteArrayInputStream(output.toByteArray());
byte[] buffer = new byte[1024];
int len = 0;
while ((len = input.read(buffer)) > 0) {
System.out.write(buffer, 0, len);
}
input.close();
}
}
在上面的代码中,我们通过ByteArrayOutputStream对象把输出转换为字节数组,在通过ByteArrayInputStream对象把字节数组转换为输入流。
当需要转换的字节流比较小时,上面的方法是一个简便的且行之有效的方法,然而当字符流比较大时,上面的代码会消耗大量的内存,甚至可能出现内存溢出的情况。
解决方法
为解决上一节提出的问题,本文实现一个字节流转换类,可以直接使用完成以上功能。基本原理是开辟一个缓冲区,通过对缓冲区并发的交互的读写操作实现内存复用和字节流的转换。
转换类代码如下:
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
public class InputOutputTransferStream extends InputStream {
private byte[] buffer = new byte[4096];
private int byteLength = 0;
private boolean writeOver = false;
private boolean readOver = false;
private InputOutputStream outputStream = new InputOutputStream();
private Object lock = new Object();
public class InputOutputStream extends OutputStream {
@Override
public void write(int c) throws IOException {
synchronized(lock) {
if (byteLength < 0)
return;
while (byteLength >= buffer.length) {
try {
lock.wait();
} catch (InterruptedException e) {
//e.printStackTrace();
}
}
if (byteLength < buffer.length) {
buffer[byteLength] = (byte)c;
byteLength++;
}
lock.notify();
}
}
@Override
public void write(byte[] buf, int offset, int len) throws IOException {
synchronized(lock) {
if (readOver)
return;
while (len > 0) {
if (byteLength >= buffer.length) {
try {
lock.wait();
} catch (InterruptedException e) {
}
}
int nLeft = buffer.length - byteLength;
int nLen = Math.min(nLeft, len);
try {
System.arraycopy(buf, offset, buffer, byteLength, nLen);
} catch (Exception ex) {
}
offset += nLen;
len -= nLen;
byteLength += nLen;
lock.notify();
}
}
}
@Override
public void write(byte[] buffer) throws IOException {
write(buffer, 0, buffer.length);
}
public void close() {
synchronized(lock) {
//byteLength = -1;
writeOver = true;
lock.notify();
}
}
}
public OutputStream getOutputStream() {
return outputStream;
}
@Override
public int read() throws IOException {
synchronized(lock) {
if (readOver)
return -1;
while (byteLength == 0) {
if (writeOver) {
throw new IOException("closed");
}
try {
lock.wait();
} catch (InterruptedException e) {
//e.printStackTrace();
}
}
if (byteLength < 0) {
return -1;
} else {
int nResult = buffer[0];
for (int i = 0; i < byteLength-1; i++) {
buffer[i] = buffer[i+1];
}
byteLength -= 1;
lock.notify();
return nResult;
}
}
}
@Override
public int read(byte[] buf, int offset, int len) throws IOException {
synchronized(lock) {
if (readOver)
return -1;
while (byteLength == 0) {
if (writeOver)
return -1;
try {
lock.wait();
} catch (InterruptedException e) {
//e.printStackTrace();
}
}
if (byteLength < 0) {
System.out.println("read -1");
return -1;
} else {
int nLen = Math.min(byteLength, len);
System.arraycopy(buffer, 0, buf, offset, nLen);
for (int i = 0; i < byteLength-nLen; i++) {
buffer[i] = buffer[i+nLen];
}
byteLength -= nLen;
lock.notify();
return nLen;
}
}
}
@Override
public int read(byte[] buffer) throws IOException {
return read(buffer, 0, buffer.length);
}
@Override
public void close() {
synchronized(lock) {
readOver = true;
//byteLength = -1;
lock.notify();
}
}
}
使用方法和测试
以下代码即可用于测试,也为该转换类使用方法的样例:
import java.io.IOException;
import java.io.PrintWriter;
public class InputOutputStreamTest {
public static void main(String[] argv) throws IOException {
final InputOutputTransferStream input = new InputOutputTransferStream();
final PrintWriter ps = new PrintWriter(input.getOutputStream());
new Thread() {
@Override
public void run() {
for (int i = 0; i < 1000; i++) {
ps.printf("This is line %d\n", i);
}
ps.close();
try {
input.getOutputStream().close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}.start();
byte[] buffer = new byte[1024];
int len = 0;
while ((len = input.read(buffer)) >= 0) {
System.out.write(buffer, 0, len);
}
input.close();
}
}
运行该代码,与原始代码实现的结果相同,执行结果均为:
This is line 0
This is line 1
This is line 2
This is line 3
This is line 4
……
This is line 995
This is line 996
This is line 997
This is line 998
This is line 999