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. 首先来看一下构造函数:

 

Java代码   收藏代码
  1. public ByteArrayOutputStream() {  
  2.         this(1024);  
  3.     }  
  4.   
  5.   
  6. public ByteArrayOutputStream(int size) {  
  7.         if (size < 0) {  
  8.             throw new IllegalArgumentException(  
  9.                 "Negative initial size: " + size);  
  10.         }  
  11.         needNewBuffer(size);  
  12.     }  
  13.    

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

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

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


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

 

 

 

Java代码   收藏代码
  1.     if ((off < 0)   
  2.             || (off > b.length)   
  3.             || (len < 0)   
  4.             || ((off + len) > b.length)   
  5.             || ((off + len) < 0)) {  
  6.         throw new IndexOutOfBoundsException();  
  7.     } else if (len == 0) {  
  8.         return;  
  9.     }  
  10.     synchronized (this) {  
  11.         int newcount = count + len;  
  12.         int remaining = len;  
  13.         int inBufferPos = count - filledBufferSum;  
  14.         while (remaining > 0) {  
  15.             int part = Math.min(remaining, currentBuffer.length - inBufferPos);  
  16.             System.arraycopy(b, off + len - remaining, currentBuffer, inBufferPos, part);  
  17.             remaining -= part;  
  18.             if (remaining > 0) {  
  19.                 needNewBuffer(newcount);  
  20.                 inBufferPos = 0;  
  21.             }  
  22.         }  
  23.         count = newcount;  
  24.     }  
  25. }  

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

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

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


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


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

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


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

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

 

 

Java代码   收藏代码
  1. int remaining = count;  
  2. if (remaining == 0) {  
  3.     return EMPTY_BYTE_ARRAY;   
  4. }  
  5. byte newbuf[] = new byte[remaining];  
  6. int pos = 0;  
  7. for (int i = 0; i < buffers.size(); i++) {  
  8.     byte[] buf = getBuffer(i);  
  9.     int c = Math.min(buf.length, remaining);  
  10.     System.arraycopy(buf, 0, newbuf, pos, c);  
  11.     pos += c;  
  12.     remaining -= c;  
  13.     if (remaining == 0) {  
  14.         break;  
  15.     }  
  16. }  
  17. return newbuf;  

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

 

下面完整的程序:

 

Java代码   收藏代码
  1. public class ByteArrayOutputStream extends OutputStream {  
  2.   
  3.     /** A singleton empty byte array. */  
  4.     private static final byte[] EMPTY_BYTE_ARRAY = new byte[0];  
  5.   
  6.     /** The list of buffers, which grows and never reduces. */  
  7.     private final List<byte[]> buffers = new ArrayList<byte[]>();  
  8.     /** The index of the current buffer. */  
  9.     private int currentBufferIndex;  
  10.     /** The total count of bytes in all the filled buffers. */  
  11.     private int filledBufferSum;  
  12.     /** The current buffer. */  
  13.     private byte[] currentBuffer;  
  14.     /** The total count of bytes written. */  
  15.     private int count;  
  16.   
  17.     /** 
  18.      * Creates a new byte array output stream. The buffer capacity is  
  19.      * initially 1024 bytes, though its size increases if necessary.  
  20.      */  
  21.     public ByteArrayOutputStream() {  
  22.         this(1024);  
  23.     }  
  24.   
  25.     /** 
  26.      * Creates a new byte array output stream, with a buffer capacity of  
  27.      * the specified size, in bytes.  
  28.      * 
  29.      * @param size  the initial size 
  30.      * @throws IllegalArgumentException if size is negative 
  31.      */  
  32.     public ByteArrayOutputStream(int size) {  
  33.         if (size < 0) {  
  34.             throw new IllegalArgumentException(  
  35.                 "Negative initial size: " + size);  
  36.         }  
  37.         needNewBuffer(size);  
  38.     }  
  39.   
  40.     /** 
  41.      * Makes a new buffer available either by allocating 
  42.      * a new one or re-cycling an existing one. 
  43.      * 
  44.      * @param newcount  the size of the buffer if one is created 
  45.      */  
  46.     private void needNewBuffer(int newcount) {  
  47.         if (currentBufferIndex < buffers.size() - 1) {  
  48.             //Recycling old buffer  
  49.             filledBufferSum += currentBuffer.length;  
  50.               
  51.             currentBufferIndex++;  
  52.             currentBuffer = buffers.get(currentBufferIndex);  
  53.         } else {  
  54.             //Creating new buffer  
  55.             int newBufferSize;  
  56.             if (currentBuffer == null) {  
  57.                 newBufferSize = newcount;  
  58.                 filledBufferSum = 0;  
  59.             } else {  
  60.                 newBufferSize = Math.max(  
  61.                     currentBuffer.length << 1,   
  62.                     newcount - filledBufferSum);  
  63.                 filledBufferSum += currentBuffer.length;  
  64.             }  
  65.               
  66.             currentBufferIndex++;  
  67.             currentBuffer = new byte[newBufferSize];  
  68.             buffers.add(currentBuffer);  
  69.         }  
  70.     }  
  71.   
  72.     /** 
  73.      * Write the bytes to byte array. 
  74.      * @param b the bytes to write 
  75.      * @param off The start offset 
  76.      * @param len The number of bytes to write 
  77.      */  
  78.     @Override  
  79.     public void write(byte[] b, int off, int len) {  
  80.         if ((off < 0)   
  81.                 || (off > b.length)   
  82.                 || (len < 0)   
  83.                 || ((off + len) > b.length)   
  84.                 || ((off + len) < 0)) {  
  85.             throw new IndexOutOfBoundsException();  
  86.         } else if (len == 0) {  
  87.             return;  
  88.         }  
  89.         synchronized (this) {  
  90.             int newcount = count + len;  
  91.             int remaining = len;  
  92.             int inBufferPos = count - filledBufferSum;  
  93.             while (remaining > 0) {  
  94.                 int part = Math.min(remaining, currentBuffer.length - inBufferPos);  
  95.                 System.arraycopy(b, off + len - remaining, currentBuffer, inBufferPos, part);  
  96.                 remaining -= part;  
  97.                 if (remaining > 0) {  
  98.                     needNewBuffer(newcount);  
  99.                     inBufferPos = 0;  
  100.                 }  
  101.             }  
  102.             count = newcount;  
  103.         }  
  104.     }  
  105.   
  106.     /** 
  107.      * Write a byte to byte array. 
  108.      * @param b the byte to write 
  109.      */  
  110.     @Override  
  111.     public synchronized void write(int b) {  
  112.         int inBufferPos = count - filledBufferSum;  
  113.         if (inBufferPos == currentBuffer.length) {  
  114.             needNewBuffer(count + 1);  
  115.             inBufferPos = 0;  
  116.         }  
  117.         currentBuffer[inBufferPos] = (byte) b;  
  118.         count++;  
  119.     }  
  120.   
  121.     /** 
  122.      * Writes the entire contents of the specified input stream to this 
  123.      * byte stream. Bytes from the input stream are read directly into the 
  124.      * internal buffers of this streams. 
  125.      * 
  126.      * @param in the input stream to read from 
  127.      * @return total number of bytes read from the input stream 
  128.      *         (and written to this stream) 
  129.      * @throws IOException if an I/O error occurs while reading the input stream 
  130.      * @since Commons IO 1.4 
  131.      */  
  132.     public synchronized int write(InputStream in) throws IOException {  
  133.         int readCount = 0;  
  134.         int inBufferPos = count - filledBufferSum;  
  135.         int n = in.read(currentBuffer, inBufferPos, currentBuffer.length - inBufferPos);  
  136.         while (n != -1) {  
  137.             readCount += n;  
  138.             inBufferPos += n;  
  139.             count += n;  
  140.             if (inBufferPos == currentBuffer.length) {  
  141.                 needNewBuffer(currentBuffer.length);  
  142.                 inBufferPos = 0;  
  143.             }  
  144.             n = in.read(currentBuffer, inBufferPos, currentBuffer.length - inBufferPos);  
  145.         }  
  146.         return readCount;  
  147.     }  
  148.   
  149.     /** 
  150.      * Return the current size of the byte array. 
  151.      * @return the current size of the byte array 
  152.      */  
  153.     public synchronized int size() {  
  154.         return count;  
  155.     }  
  156.   
  157.     /** 
  158.      * Closing a <tt>ByteArrayOutputStream</tt> has no effect. The methods in 
  159.      * this class can be called after the stream has been closed without 
  160.      * generating an <tt>IOException</tt>. 
  161.      * 
  162.      * @throws IOException never (this method should not declare this exception 
  163.      * but it has to now due to backwards compatability) 
  164.      */  
  165.     @Override  
  166.     public void close() throws IOException {  
  167.         //nop  
  168.     }  
  169.   
  170.     /** 
  171.      * @see java.io.ByteArrayOutputStream#reset() 
  172.      */  
  173.     public synchronized void reset() {  
  174.         count = 0;  
  175.         filledBufferSum = 0;  
  176.         currentBufferIndex = 0;  
  177.         currentBuffer = buffers.get(currentBufferIndex);  
  178.     }  
  179.   
  180.     /** 
  181.      * Writes the entire contents of this byte stream to the 
  182.      * specified output stream. 
  183.      * 
  184.      * @param out  the output stream to write to 
  185.      * @throws IOException if an I/O error occurs, such as if the stream is closed 
  186.      * @see java.io.ByteArrayOutputStream#writeTo(OutputStream) 
  187.      */  
  188.     public synchronized void writeTo(OutputStream out) throws IOException {  
  189.         int remaining = count;  
  190.         for (byte[] buf : buffers) {  
  191.             int c = Math.min(buf.length, remaining);  
  192.             out.write(buf, 0, c);  
  193.             remaining -= c;  
  194.             if (remaining == 0) {  
  195.                 break;  
  196.             }  
  197.         }  
  198.     }  
  199.   
  200.     /** 
  201.      * Fetches entire contents of an <code>InputStream</code> and represent 
  202.      * same data as result InputStream. 
  203.      * <p> 
  204.      * This method is useful where, 
  205.      * <ul> 
  206.      * <li>Source InputStream is slow.</li> 
  207.      * <li>It has network resources associated, so we cannot keep it open for 
  208.      * long time.</li> 
  209.      * <li>It has network timeout associated.</li> 
  210.      * </ul> 
  211.      * It can be used in favor of {@link #toByteArray()}, since it 
  212.      * avoids unnecessary allocation and copy of byte[].<br> 
  213.      * This method buffers the input internally, so there is no need to use a 
  214.      * <code>BufferedInputStream</code>. 
  215.      *  
  216.      * @param input Stream to be fully buffered. 
  217.      * @return A fully buffered stream. 
  218.      * @throws IOException if an I/O error occurs 
  219.      */  
  220.     public static InputStream toBufferedInputStream(InputStream input)  
  221.             throws IOException {  
  222.         ByteArrayOutputStream output = new ByteArrayOutputStream();  
  223.         output.write(input);  
  224.         return output.toBufferedInputStream();  
  225.     }  
  226.   
  227.     /** 
  228.      * Gets the current contents of this byte stream as a Input Stream. The 
  229.      * returned stream is backed by buffers of <code>this</code> stream, 
  230.      * avoiding memory allocation and copy, thus saving space and time.<br> 
  231.      *  
  232.      * @return the current contents of this output stream. 
  233.      * @see java.io.ByteArrayOutputStream#toByteArray() 
  234.      * @see #reset() 
  235.      * @since Commons IO 2.0 
  236.      */  
  237.     private InputStream toBufferedInputStream() {  
  238.         int remaining = count;  
  239.         if (remaining == 0) {  
  240.             return new ClosedInputStream();  
  241.         }  
  242.         List<ByteArrayInputStream> list = new ArrayList<ByteArrayInputStream>(buffers.size());  
  243.         for (byte[] buf : buffers) {  
  244.             int c = Math.min(buf.length, remaining);  
  245.             list.add(new ByteArrayInputStream(buf, 0, c));  
  246.             remaining -= c;  
  247.             if (remaining == 0) {  
  248.                 break;  
  249.             }  
  250.         }  
  251.         return new SequenceInputStream(Collections.enumeration(list));  
  252.     }  
  253.   
  254.     /** 
  255.      * Gets the curent contents of this byte stream as a byte array. 
  256.      * The result is independent of this stream. 
  257.      * 
  258.      * @return the current contents of this output stream, as a byte array 
  259.      * @see java.io.ByteArrayOutputStream#toByteArray() 
  260.      */  
  261.     public synchronized byte[] toByteArray() {  
  262.         int remaining = count;  
  263.         if (remaining == 0) {  
  264.             return EMPTY_BYTE_ARRAY;   
  265.         }  
  266.         byte newbuf[] = new byte[remaining];  
  267.         int pos = 0;  
  268.         for (byte[] buf : buffers) {  
  269.             int c = Math.min(buf.length, remaining);  
  270.             System.arraycopy(buf, 0, newbuf, pos, c);  
  271.             pos += c;  
  272.             remaining -= c;  
  273.             if (remaining == 0) {  
  274.                 break;  
  275.             }  
  276.         }  
  277.         return newbuf;  
  278.     }  
  279.   
  280.     /** 
  281.      * Gets the curent contents of this byte stream as a string. 
  282.      * @return the contents of the byte array as a String 
  283.      * @see java.io.ByteArrayOutputStream#toString() 
  284.      */  
  285.     @Override  
  286.     public String toString() {  
  287.         return new String(toByteArray());  
  288.     }  
  289.   
  290.     /** 
  291.      * Gets the curent contents of this byte stream as a string 
  292.      * using the specified encoding. 
  293.      * 
  294.      * @param enc  the name of the character encoding 
  295.      * @return the string converted from the byte array 
  296.      * @throws UnsupportedEncodingException if the encoding is not supported 
  297.      * @see java.io.ByteArrayOutputStream#toString(String) 
  298.      */  
  299.     public String toString(String enc) throws UnsupportedEncodingException {  
  300.         return new String(toByteArray(), enc);  
  301.     }  
  302.   
  303. }  
 
ByteArrayOutputStream的close方法是空实现,调用该方法并不会真正关闭输出流。关闭ByteArrayOutputStream对于内存流来说没有实际意义,因为它不涉及硬盘或网络的操作,而是基于内存的流。当使用完毕后,ByteArrayOutputStream会被垃圾回收机制清理掉。因此,即使调用了close方法,之后仍然可以对该输出流进行其他操作而不会抛出IOException异常。\[1\] #### 引用[.reference_title] - *1* [ByteArrayOutputStreamByteArrayInputStream不需要关闭](https://blog.csdn.net/qq_42747210/article/details/113063110)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down1,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [Java IO类库之ByteArrayOutputStream](https://blog.csdn.net/weixin_34092370/article/details/92608768)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down1,239^v3^insert_chatgpt"}} ] [.reference_item] - *3* [Java ByteArrayOutputStream close()方法与示例](https://blog.csdn.net/cumt30111/article/details/107766708)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down1,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值