`

common-io 之ByteArrayOutputStream阅读

阅读更多


首先我们来分析它里面的的实例变量:

buffers: 可以看成是一个buffer仓库,里面放的是已经读取的所有数据
currentBufferIndex: 就是正在使用的buffer的index
count: 用来存放buffers里面的所有的字节数
currentBuffer: 就是当前的使用buffer,这个比较好理解。
filledBufferSum: 这个起初的时候我特别不理解,后来我理解,主要是用了保存所有的满buffer的字节数的总和。

举个例子:
          起始的时候第一个buffer的大小为32,它的filledBufferSum为0,count为0,然后我们给当前的buffer放入5个字节的数据,现在count为5, 下一次我们count - filedBufferSum 就是我们下一次要存储的buffer的指针,比如我们要放25个字节,那么现在count就变成30了,filledBufferSum仍然是0,我们再放入3个字节,现在count变成33,比buffer的初始大小大了,就扩容,新建一个buffer,把老的buffer放到buffers里面,然后filledBufferSum就变成32了,把扩容后剩余的1个字节放到新申请的buffer里面,下一次比如我们想再放入10个字节的数据,count是33,filedBufferSum是32,我们存放的指针应该是1,因为0字节我们存放了上次扩容后的剩余的字节数。


1. 首先来看一下构造函数:

 

public ByteArrayOutputStream() {
        this(1024);
    }


public ByteArrayOutputStream(int size) {
        if (size < 0) {
            throw new IllegalArgumentException(
                "Negative initial size: " + size);
        }
        needNewBuffer(size);
    }
 

 
    一个是无参数的时候创建一个大小为1024的buffer,一个是根据用户输入的大小创建buffer,这个都比较好理解,关键是needNewBuffer函数,这个放到下面进行讲解。

2. 下面来看一下needNewBuffer函数,这个是这个类的灵魂,我感觉

private void needNewBuffer(int newcount) {
        if (currentBufferIndex < buffers.size() - 1) {
            //Recycling old buffer
            filledBufferSum += currentBuffer.length;
            
            currentBufferIndex++;
            currentBuffer = getBuffer(currentBufferIndex);
        } else {
            //Creating new buffer
            int newBufferSize;
            if (currentBuffer == null) {
                newBufferSize = newcount;
                filledBufferSum = 0;
            } else {
                newBufferSize = Math.max(
                    currentBuffer.length << 1, 
                    newcount - filledBufferSum);
                filledBufferSum += currentBuffer.length;
            }
            
            currentBufferIndex++;
            currentBuffer = new byte[newBufferSize];
            buffers.add(currentBuffer);
        }
    }
 
      首先我们为了好理解期间,我们先来讲else分支,if分支我们放到reset函数里讲解。
这个就是我们传统意义上的创建新的buffer。这个里面如果currentBuffer是null的话,
就初始化一个,这个分支是在创建的时候会走这个分支,下一个分支是将当前的buffer
的length乘以2和所需要的大小进行比较取最大值来当成新的buffer的大小,
然后把filledBufferSum的值进行更改。下面几行代码是来真正创建buffer的地方,
并把他加到buffers里面去。


3. 下面我们来看一下write函数
public void write(byte[] b, int off, int len) {

 

 

 

        if ((off < 0) 
                || (off > b.length) 
                || (len < 0) 
                || ((off + len) > b.length) 
                || ((off + len) < 0)) {
            throw new IndexOutOfBoundsException();
        } else if (len == 0) {
            return;
        }
        synchronized (this) {
            int newcount = count + len;
            int remaining = len;
            int inBufferPos = count - filledBufferSum;
            while (remaining > 0) {
                int part = Math.min(remaining, currentBuffer.length - inBufferPos);
                System.arraycopy(b, off + len - remaining, currentBuffer, inBufferPos, part);
                remaining -= part;
                if (remaining > 0) {
                    needNewBuffer(newcount);
                    inBufferPos = 0;
                }
            }
            count = newcount;
        }
    }

 
       首先是一些检验,放在数组越界,这些判断是和父类里面的判断是相同的。
下面是才真正的实现写操作。 首先来计算新的count,并将要写的字节数当成初始的remaining,并来计算这次要写的指针位置, 就是上次的总大小减去已经存放满的buffer里面的字节数。
      在这里将remaining和当前所剩余的空间做了一个比较,取最小值。然后做数组拷贝动作。然后判断是不是已经完全写完,如果没写完的话就是分配空间了,然后执行分配空间动作,最后在循环的写入到buffer里面。

4. 下面我们在巩固一下,理解一下write函数

  /**
     * Write a byte to byte array.
     * @param b the byte to write
     */
    public synchronized void write(int b) {
        int inBufferPos = count - filledBufferSum;
        if (inBufferPos == currentBuffer.length) {
            needNewBuffer(count + 1);
            inBufferPos = 0;
        }
        currentBuffer[inBufferPos] = (byte) b;
        count++;
    }
 
      只要理解了上面的,这个函数就特别好理解,首先是得到这次应该写入的指针位置,如果发现没有空间的话进行分配新的空间,然后把数据写到buffer里面,并累加写入的数据总数。


5. write函数的研究
public synchronized int write(InputStream in) throws IOException {
        int readCount = 0;
        int inBufferPos = count - filledBufferSum;
        int n = in.read(currentBuffer, inBufferPos, currentBuffer.length - inBufferPos);
        while (n != -1) {
            readCount += n;
            inBufferPos += n;
            count += n;
            if (inBufferPos == currentBuffer.length) {
                needNewBuffer(currentBuffer.length);
                inBufferPos = 0;
            }
            n = in.read(currentBuffer, inBufferPos, currentBuffer.length - inBufferPos);
        }
        return readCount;
    }
 
          这个函数其实也比较好理解,主要是计算应该写入的指针位置,然后从InputStream里面读到currentBuffer里面,从inBufferPos开始,总共读最大剩余长度个字节,如果读到的不为-1,则变化当前的指针和readCount,然后如果当前的指针和currentBuffer的length相等时,则进行扩充缓冲区,然后进行循环,直到读完。


6. reset函数
public synchronized void reset() {
        count = 0;
        filledBufferSum = 0;
        currentBufferIndex = 0;
        currentBuffer = getBuffer(currentBufferIndex);
    }
 
       我起始得时候不理解,现在理解了。比如我们首先对第一个文件进行了写操作,然后缓冲区并没有释放,我们要读下一个文件,那么我们现在肯定不能再去申请空间了,要对上一次的缓冲区进行复用,这里就先将一些状态变量清0,然后将当前的buffer设置为第0个buffer,这样下一次操作的时候就可以使用上次分配的buffer了。为了把问题说明白,我们再来看一下
needNewBuffer的if分支,
if (currentBufferIndex < buffers.size() - 1) {
            //Recycling old buffer
            filledBufferSum += currentBuffer.length;
            
            currentBufferIndex++;
            currentBuffer = getBuffer(currentBufferIndex);
 
 

这个里面,我们可以看到调用了这个函数后,currentBufferIndex是0,如果buffers还有其他缓冲区的话就将当前的bufferIndex加1,并返回下一个缓冲区,这样感觉效率比较高,到这里我们估计你应该彻底理解这个里面的流程了。


7. 下面我们来看一下writeTo函数的实现
 public synchronized void writeTo(OutputStream out) throws IOException {
        int remaining = count;
        for (int i = 0; i < buffers.size(); i++) {
            byte[] buf = getBuffer(i);
            int c = Math.min(buf.length, remaining);
            out.write(buf, 0, c);
            remaining -= c;
            if (remaining == 0) {
                break;
            }
        }
    }
 
      这个函数主要是实现了把buf中的数据直接写到用户指定的OutputStream里面。这个里面和JDK里面的实现的唯一的差别就是这里会循环使得buffers中的数据会全部写到指定的OutputStream里面。

8. 下面我们来发一下toByteArray的实现。
public synchronized byte[] toByteArray() {

 

 

        int remaining = count;
        if (remaining == 0) {
            return EMPTY_BYTE_ARRAY; 
        }
        byte newbuf[] = new byte[remaining];
        int pos = 0;
        for (int i = 0; i < buffers.size(); i++) {
            byte[] buf = getBuffer(i);
            int c = Math.min(buf.length, remaining);
            System.arraycopy(buf, 0, newbuf, pos, c);
            pos += c;
            remaining -= c;
            if (remaining == 0) {
                break;
            }
        }
        return newbuf;
    }

 
      这个函数很好理解,就是创建一个count大小的byte数组,然后循环buffers,将每一个缓冲区中的数据都copy到将要返回的字节数组里面。

 

下面完整的程序:

 

public class ByteArrayOutputStream extends OutputStream {

    /** A singleton empty byte array. */
    private static final byte[] EMPTY_BYTE_ARRAY = new byte[0];

    /** The list of buffers, which grows and never reduces. */
    private final List<byte[]> buffers = new ArrayList<byte[]>();
    /** The index of the current buffer. */
    private int currentBufferIndex;
    /** The total count of bytes in all the filled buffers. */
    private int filledBufferSum;
    /** The current buffer. */
    private byte[] currentBuffer;
    /** The total count of bytes written. */
    private int count;

    /**
     * Creates a new byte array output stream. The buffer capacity is 
     * initially 1024 bytes, though its size increases if necessary. 
     */
    public ByteArrayOutputStream() {
        this(1024);
    }

    /**
     * Creates a new byte array output stream, with a buffer capacity of 
     * the specified size, in bytes. 
     *
     * @param size  the initial size
     * @throws IllegalArgumentException if size is negative
     */
    public ByteArrayOutputStream(int size) {
        if (size < 0) {
            throw new IllegalArgumentException(
                "Negative initial size: " + size);
        }
        needNewBuffer(size);
    }

    /**
     * Makes a new buffer available either by allocating
     * a new one or re-cycling an existing one.
     *
     * @param newcount  the size of the buffer if one is created
     */
    private void needNewBuffer(int newcount) {
        if (currentBufferIndex < buffers.size() - 1) {
            //Recycling old buffer
            filledBufferSum += currentBuffer.length;
            
            currentBufferIndex++;
            currentBuffer = buffers.get(currentBufferIndex);
        } else {
            //Creating new buffer
            int newBufferSize;
            if (currentBuffer == null) {
                newBufferSize = newcount;
                filledBufferSum = 0;
            } else {
                newBufferSize = Math.max(
                    currentBuffer.length << 1, 
                    newcount - filledBufferSum);
                filledBufferSum += currentBuffer.length;
            }
            
            currentBufferIndex++;
            currentBuffer = new byte[newBufferSize];
            buffers.add(currentBuffer);
        }
    }

    /**
     * Write the bytes to byte array.
     * @param b the bytes to write
     * @param off The start offset
     * @param len The number of bytes to write
     */
    @Override
    public void write(byte[] b, int off, int len) {
        if ((off < 0) 
                || (off > b.length) 
                || (len < 0) 
                || ((off + len) > b.length) 
                || ((off + len) < 0)) {
            throw new IndexOutOfBoundsException();
        } else if (len == 0) {
            return;
        }
        synchronized (this) {
            int newcount = count + len;
            int remaining = len;
            int inBufferPos = count - filledBufferSum;
            while (remaining > 0) {
                int part = Math.min(remaining, currentBuffer.length - inBufferPos);
                System.arraycopy(b, off + len - remaining, currentBuffer, inBufferPos, part);
                remaining -= part;
                if (remaining > 0) {
                    needNewBuffer(newcount);
                    inBufferPos = 0;
                }
            }
            count = newcount;
        }
    }

    /**
     * Write a byte to byte array.
     * @param b the byte to write
     */
    @Override
    public synchronized void write(int b) {
        int inBufferPos = count - filledBufferSum;
        if (inBufferPos == currentBuffer.length) {
            needNewBuffer(count + 1);
            inBufferPos = 0;
        }
        currentBuffer[inBufferPos] = (byte) b;
        count++;
    }

    /**
     * Writes the entire contents of the specified input stream to this
     * byte stream. Bytes from the input stream are read directly into the
     * internal buffers of this streams.
     *
     * @param in the input stream to read from
     * @return total number of bytes read from the input stream
     *         (and written to this stream)
     * @throws IOException if an I/O error occurs while reading the input stream
     * @since Commons IO 1.4
     */
    public synchronized int write(InputStream in) throws IOException {
        int readCount = 0;
        int inBufferPos = count - filledBufferSum;
        int n = in.read(currentBuffer, inBufferPos, currentBuffer.length - inBufferPos);
        while (n != -1) {
            readCount += n;
            inBufferPos += n;
            count += n;
            if (inBufferPos == currentBuffer.length) {
                needNewBuffer(currentBuffer.length);
                inBufferPos = 0;
            }
            n = in.read(currentBuffer, inBufferPos, currentBuffer.length - inBufferPos);
        }
        return readCount;
    }

    /**
     * Return the current size of the byte array.
     * @return the current size of the byte array
     */
    public synchronized int size() {
        return count;
    }

    /**
     * Closing a <tt>ByteArrayOutputStream</tt> has no effect. The methods in
     * this class can be called after the stream has been closed without
     * generating an <tt>IOException</tt>.
     *
     * @throws IOException never (this method should not declare this exception
     * but it has to now due to backwards compatability)
     */
    @Override
    public void close() throws IOException {
        //nop
    }

    /**
     * @see java.io.ByteArrayOutputStream#reset()
     */
    public synchronized void reset() {
        count = 0;
        filledBufferSum = 0;
        currentBufferIndex = 0;
        currentBuffer = buffers.get(currentBufferIndex);
    }

    /**
     * Writes the entire contents of this byte stream to the
     * specified output stream.
     *
     * @param out  the output stream to write to
     * @throws IOException if an I/O error occurs, such as if the stream is closed
     * @see java.io.ByteArrayOutputStream#writeTo(OutputStream)
     */
    public synchronized void writeTo(OutputStream out) throws IOException {
        int remaining = count;
        for (byte[] buf : buffers) {
            int c = Math.min(buf.length, remaining);
            out.write(buf, 0, c);
            remaining -= c;
            if (remaining == 0) {
                break;
            }
        }
    }

    /**
     * Fetches entire contents of an <code>InputStream</code> and represent
     * same data as result InputStream.
     * <p>
     * This method is useful where,
     * <ul>
     * <li>Source InputStream is slow.</li>
     * <li>It has network resources associated, so we cannot keep it open for
     * long time.</li>
     * <li>It has network timeout associated.</li>
     * </ul>
     * It can be used in favor of {@link #toByteArray()}, since it
     * avoids unnecessary allocation and copy of byte[].<br>
     * This method buffers the input internally, so there is no need to use a
     * <code>BufferedInputStream</code>.
     * 
     * @param input Stream to be fully buffered.
     * @return A fully buffered stream.
     * @throws IOException if an I/O error occurs
     */
    public static InputStream toBufferedInputStream(InputStream input)
            throws IOException {
        ByteArrayOutputStream output = new ByteArrayOutputStream();
        output.write(input);
        return output.toBufferedInputStream();
    }

    /**
     * Gets the current contents of this byte stream as a Input Stream. The
     * returned stream is backed by buffers of <code>this</code> stream,
     * avoiding memory allocation and copy, thus saving space and time.<br>
     * 
     * @return the current contents of this output stream.
     * @see java.io.ByteArrayOutputStream#toByteArray()
     * @see #reset()
     * @since Commons IO 2.0
     */
    private InputStream toBufferedInputStream() {
        int remaining = count;
        if (remaining == 0) {
            return new ClosedInputStream();
        }
        List<ByteArrayInputStream> list = new ArrayList<ByteArrayInputStream>(buffers.size());
        for (byte[] buf : buffers) {
            int c = Math.min(buf.length, remaining);
            list.add(new ByteArrayInputStream(buf, 0, c));
            remaining -= c;
            if (remaining == 0) {
                break;
            }
        }
        return new SequenceInputStream(Collections.enumeration(list));
    }

    /**
     * Gets the curent contents of this byte stream as a byte array.
     * The result is independent of this stream.
     *
     * @return the current contents of this output stream, as a byte array
     * @see java.io.ByteArrayOutputStream#toByteArray()
     */
    public synchronized byte[] toByteArray() {
        int remaining = count;
        if (remaining == 0) {
            return EMPTY_BYTE_ARRAY; 
        }
        byte newbuf[] = new byte[remaining];
        int pos = 0;
        for (byte[] buf : buffers) {
            int c = Math.min(buf.length, remaining);
            System.arraycopy(buf, 0, newbuf, pos, c);
            pos += c;
            remaining -= c;
            if (remaining == 0) {
                break;
            }
        }
        return newbuf;
    }

    /**
     * Gets the curent contents of this byte stream as a string.
     * @return the contents of the byte array as a String
     * @see java.io.ByteArrayOutputStream#toString()
     */
    @Override
    public String toString() {
        return new String(toByteArray());
    }

    /**
     * Gets the curent contents of this byte stream as a string
     * using the specified encoding.
     *
     * @param enc  the name of the character encoding
     * @return the string converted from the byte array
     * @throws UnsupportedEncodingException if the encoding is not supported
     * @see java.io.ByteArrayOutputStream#toString(String)
     */
    public String toString(String enc) throws UnsupportedEncodingException {
        return new String(toByteArray(), enc);
    }

}
 


2
0
分享到:
评论

相关推荐

    【IT十八掌徐培成】Java基础第16天-04.ByteArrayInputStream-ByteArrayOutputStream.zip

    【IT十八掌徐培成】Java基础第16天-04.ByteArrayInputStream-ByteArrayOutputStream.zip

    Java-IO-Learning

    Java IO学习 FileInputStream,FileOutputstream 随机存取文件 ByteArrayOutputStream,ByteArrayInputStream

    JAVA IO流缓冲字节流缓冲字符流等流经典代码示例加注释总结.rar

    2、常用21个IO流:FileWriter、FileReader、CharArrayReader、CharArrayWriter、CharSequence、OutputStreamWriter、FileOutputStream、InputStreamReader、PrintWriter、BufferedReader、InputStream、...

    ByteArrayOutputStream简介和使用_动力节点Java学院整理

    ByteArrayOutputStream 是字节数组输出流。它继承于OutputStream。 ByteArrayOutputStream 中的数据被写入一个 byte 数组。缓冲区会随着数据的不断写入而自动增长。可使用 toByteArray() 和 toString() 获取数据。

    教你彻底明白Java的IO系统

    在Java的IO中,所有的stream(包括Input和Out stream)都包括两种类型: 1.1 以字节为导向的stream 以字节为导向的stream,表示以字节为单位从stream中读取或往stream中写入信息。以字节为导向的stream包括下面几种...

    IO体系.java

    InputStream ... |--ByteArrayOutputStream/:实现了一个输出流,其中的数据写入到一个byte数组。 | 使用toByteArray()和toString()获取数据。关闭无效,依然可以调用。不会产生任何IOException异常。

    Android字符串和十六进制相互转化出现的中文乱码问题

    import java.io.ByteArrayOutputStream; /** * Created by Administrator on 2016/2/2. * -----------16进制和字符串互转--------- * ------------解决中文乱码问题--------- */ public class StringToSixthUtils { ...

    深刻理解java io

    1) ByteArrayOutputStream:把信息存入内存中的一个缓冲区中 2) FileOutputStream:把信息存入文件中 3) PipedOutputStream:实现了pipe的概念,主要在线程中使用 4) SequenceOutputStream:把多个OutStream合并为一...

    Java之IO流学习总结

    ByteArrayOutputStream、FileOutputStream 是两种基本的介质流,它们分别向Byte 数组、和本地文件中写入数据。PipedOutputStream 是向与其它线程共用的管道中写入数据, ObjectOutputStream 和所有...

    BSONforJackson.zip

    import java.io.ByteArrayOutputStream; import com.fasterxml.jackson.databind.ObjectMapper; import de.undercouch.bson4jackson.BsonFactory; public class ObjectMapperSample {  public static void...

    ftp网络下载

    import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.net.HttpURLConnection; import java.net....

    Android ByteArrayOutputStream() 读取TXT文件并输出.rar

    Android ByteArrayOutputStream() 读取TXT文件并输出,Andorid文件流方式打开文本文件的例子,在文本框中输入文件路径,点击按钮后直接读取文件内容,并输出到屏幕上显示,是一个比较基础的Java读取txt文件的例子,...

    java io读取文件到String

    ByteArrayOutputStream outstream = new ByteArrayOutputStream( contentLength &gt; 0 ? (int) contentLength : 1024); byte[] buffer = new byte[4096]; int len; while ((len = is.read(buffer)) &gt; 0) { ...

    Android实现压缩字符串的方法示例

    前言 Android端可以对字符串进行压缩,我们在进行大量简单文本传输时...import java.io.ByteArrayOutputStream; import java.io.IOException; import java.util.zip.GZIPInputStream; import java.util.zip.GZIPOutput

    文件生成图片

    import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileOutputStream; &lt;br&gt;import com.sun.image.codec.jpeg.JPEGCodec; import ...

    java从输入流中获取数据并返回字节数组示例

    代码如下:import java.io.ByteArrayOutputStream;import java.io.InputStream;//从输入流中获取数据并以字节数组返回public class StreamTool { /** * 从输入流获取数据 * @param inputStream * @return * @...

    Android 异步获取网络图片并处理导致内存溢出问题解决方法

    测试环境为Adnroid 2.1...异步图片类 ImageDownloadTask 代码如下: import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.net.MalformedURLException; impo

    Java实现远程屏幕监视

    import java.io.ByteArrayOutputStream; import java.io.DataOutputStream; import java.io.IOException; import java.net.Socket; import java.util.zip.ZipEntry; import java.util.zip.ZipOutputStream; import ...

    IO流各个类的使用方法

    介绍了,下面这些类的具体使用方法,非常详细,值得一...ByteArrayOutputStream 【字符流】 Reader Writer InputStreamReader OutputStreamWriter BufferedReader BufferedWriter /PrintWriter FileReader FileWriter

Global site tag (gtag.js) - Google Analytics