Java Tutorial/File/ByteArrayOutputStream

Материал из Java эксперт
Перейти к: навигация, поиск

A speedy implementation of ByteArrayOutputStream.

   <source lang="java">

/*

* Copyright (c) 2002-2003 by OpenSymphony
* All rights reserved.
*/

import java.io.IOException; import java.io.OutputStream; import java.io.RandomAccessFile; import java.io.Writer; import java.util.Iterator; import java.util.LinkedList;

/**

* A speedy implementation of ByteArrayOutputStream. It"s not synchronized, and it
* does not copy buffers when it"s expanded. There"s also no copying of the internal buffer
* if it"s contents is extracted with the writeTo(stream) method.
*
* @author Rickard ?berg
* @author Brat Baker (Atlassian)
* @author Alexey
* @version $Date: 2008-01-19 10:09:56 +0800 (Sat, 19 Jan 2008) $ $Id: FastByteArrayOutputStream.java 3000 2008-01-19 02:09:56Z tm_jee $
*/

public class FastByteArrayOutputStream extends OutputStream {

   // Static --------------------------------------------------------
   private static final int DEFAULT_BLOCK_SIZE = 8192;
   private LinkedList buffers;
   // Attributes ----------------------------------------------------
   // internal buffer
   private byte[] buffer;
   // is the stream closed?
   private boolean closed;
   private int blockSize;
   private int index;
   private int size;
   // Constructors --------------------------------------------------
   public FastByteArrayOutputStream() {
       this(DEFAULT_BLOCK_SIZE);
   }
   public FastByteArrayOutputStream(int aSize) {
       blockSize = aSize;
       buffer = new byte[blockSize];
   }
   public int getSize() {
       return size + index;
   }
   public void close() {
       closed = true;
   }
   public byte[] toByteArray() {
       byte[] data = new byte[getSize()];
       // Check if we have a list of buffers
       int pos = 0;
       if (buffers != null) {
           Iterator iter = buffers.iterator();
           while (iter.hasNext()) {
               byte[] bytes = (byte[]) iter.next();
               System.arraycopy(bytes, 0, data, pos, blockSize);
               pos += blockSize;
           }
       }
       // write the internal buffer directly
       System.arraycopy(buffer, 0, data, pos, index);
       return data;
   }
   public String toString() {
       return new String(toByteArray());
   }
   // OutputStream overrides ----------------------------------------
   public void write(int datum) throws IOException {
       if (closed) {
           throw new IOException("Stream closed");
       } else {
           if (index == blockSize) {
               addBuffer();
           }
           // store the byte
           buffer[index++] = (byte) datum;
       }
   }
   public void write(byte[] data, int offset, int length) throws IOException {
       if (data == null) {
           throw new NullPointerException();
       } else if ((offset < 0) || ((offset + length) > data.length) || (length < 0)) {
           throw new IndexOutOfBoundsException();
       } else if (closed) {
           throw new IOException("Stream closed");
       } else {
           if ((index + length) > blockSize) {
               int copyLength;
               do {
                   if (index == blockSize) {
                       addBuffer();
                   }
                   copyLength = blockSize - index;
                   if (length < copyLength) {
                       copyLength = length;
                   }
                   System.arraycopy(data, offset, buffer, index, copyLength);
                   offset += copyLength;
                   index += copyLength;
                   length -= copyLength;
               } while (length > 0);
           } else {
               // Copy in the subarray
               System.arraycopy(data, offset, buffer, index, length);
               index += length;
           }
       }
   }
   // Public
   public void writeTo(OutputStream out) throws IOException {
       // Check if we have a list of buffers
       if (buffers != null) {
           Iterator iter = buffers.iterator();
           while (iter.hasNext()) {
               byte[] bytes = (byte[]) iter.next();
               out.write(bytes, 0, blockSize);
           }
       }
       // write the internal buffer directly
       out.write(buffer, 0, index);
   }
   public void writeTo(RandomAccessFile out) throws IOException {
       // Check if we have a list of buffers
       if (buffers != null) {
           Iterator iter = buffers.iterator();
           while (iter.hasNext()) {
               byte[] bytes = (byte[]) iter.next();
               out.write(bytes, 0, blockSize);
           }
       }
       // write the internal buffer directly
       out.write(buffer, 0, index);
   }
   public void writeTo(Writer out, String encoding) throws IOException {
       /*
         There is design tradeoff between being fast, correct and using too much memory when decoding bytes to strings.
        The rules are thus :
        1. if there is only one buffer then its a simple String conversion
             REASON : Fast!!!
        2. uses full buffer allocation annd System.arrayCopy() to smooosh together the bytes
             and then use String conversion
             REASON : Fast at the expense of a known amount of memory (eg the used memory * 2)
       */
       if (buffers != null)
       {
           // RULE 2 : a balance between using some memory and speed
           writeToViaSmoosh(out, encoding);
       }
       else
       {
           // RULE 1 : fastest!
           writeToViaString(out, encoding);
       }
   }
   /**
    * This can ONLY be called if there is only a single buffer to write, instead
    * use {@link #writeTo(java.io.Writer, String)}, which auto detects if
    * {@link #writeToViaString(java.io.Writer, String)} is to be used or
    * {@link #writeToViaSmoosh(java.io.Writer, String)}.
    *
    * @param out      the JspWriter
    * @param encoding the encoding
    * @throws IOException
    */
   void writeToViaString(Writer out, String encoding) throws IOException
   {
       byte[] bufferToWrite = buffer; // this is always the last buffer to write
       int bufferToWriteLen = index;  // index points to our place in the last buffer
       writeToImpl(out, encoding, bufferToWrite, bufferToWriteLen);
   }
   /**
    * This is recommended to be used where there"s more than 1 buffer to write, instead
    * use {@link #writeTo(java.io.Writer, String)} which auto detects if
    * {@link #writeToViaString(java.io.Writer, String)} is to be used or
    * {@link #writeToViaSmoosh(java.io.Writer, String)}.
    * 
    * @param out
    * @param encoding
    * @throws IOException
    */
   void writeToViaSmoosh(Writer out, String encoding) throws IOException
   {
       byte[] bufferToWrite = toByteArray();
       int bufferToWriteLen = bufferToWrite.length;
       writeToImpl(out, encoding, bufferToWrite, bufferToWriteLen);
   }
   /**
    * Write bufferToWriteLen of bytes from bufferToWrite to
    * out encoding it at the same time.
    * 
    * @param out
    * @param encoding
    * @param bufferToWrite
    * @param bufferToWriteLen
    * @throws IOException
    */
   private void writeToImpl(Writer out, String encoding, byte[] bufferToWrite, int bufferToWriteLen)
           throws IOException
   {
       String writeStr;
       if (encoding != null)
       {
           writeStr = new String(bufferToWrite, 0, bufferToWriteLen, encoding);
       }
       else
       {
           writeStr = new String(bufferToWrite, 0, bufferToWriteLen);
       }
       out.write(writeStr);
   }
   /**
    * Create a new buffer and store the
    * current one in linked list
    */
   protected void addBuffer() {
       if (buffers == null) {
           buffers = new LinkedList();
       }
       buffers.addLast(buffer);
       buffer = new byte[blockSize];
       size += index;
       index = 0;
   }

} ////////////////////////////

import java.io.IOException; import javax.servlet.jsp.JspWriter; /**

* A test class for {@link webwork.util.FastByteArrayOutputStream}
*
* @author Brad Baker (Atlassian)
* @since $Date$ $Id$
*/

public class FastByteArrayOutputStreamTestCase extends AbstractEncodingTestCase {

   public void testLatinCharsets() throws Exception
   {
       assertEncoding(ASCII_TEXT, LATIN);
       assertEncoding(ASCII_TEXT, ASCII);
   }
   public void testRussianCharsets() throws Exception
   {
       assertEncoding(RUSSIAN_DESC_SHORT, KOI8_R);
       assertEncoding(RUSSIAN_DESC1, KOI8_R);
       assertEncoding(RUSSIAN_DESC_SHORT, WINDOWS_CYRILLIC);
       assertEncoding(RUSSIAN_DESC1, WINDOWS_CYRILLIC);
   }
   public void testUnicodeCharsets() throws Exception
   {
       String[] testStrs = {ASCII_TEXT_SHORT, ASCII_TEXT, RUSSIAN_DESC_SHORT, RUSSIAN_DESC1, CHINESE_TIMETRACKING, HUNGRIAN_APPLET_PROBLEM, };
       String[] encodings = { UTF_8, UTF_16, UBIG_ENDIAN, ULITTLE_ENDIAN, UBIG_ENDIAN_UNMARKED, ULITTLE_ENDIAN_UNMARKED };
       assertEncodings(testStrs, encodings);
   }
   protected void implementEncodingTest(final String srcStr, final String encoding, final int bufferSize)
           throws Exception
   {
       FastByteArrayOutputStream bout = new FastByteArrayOutputStream(bufferSize);
       byte[] bytes = srcStr.getBytes(encoding);
       bout.write(bytes);
       JspWriter writer = new StringCapturingJspWriter();
       bout.writeTo(writer, encoding);
       String actualStr = writer.toString();
       String expectedStr = new String(bytes, encoding);
       assertTheyAreEqual(expectedStr, actualStr, encoding);
   }
   /**
    * Before it was changed to use {@link java.nio.charset.CharsetDecoder} is took an this time
    * <p/>
    * Total Call Time = 1112.0ms
    * Average Call Time = 0.001112ms
    * <p/>
    * Now with the change it takes this time
    * <p/>
    * <p/>
    * The idea is that it did not get significantly worse in performance
    *
    * @throws IOException
    */
   public void testPerformanceOfWriteToJspWriter() throws IOException
   {
       final String NINEK_STR = makeRoughly(ASCII, 9 * K);
       final String FIFTYK_STR = makeRoughly(ASCII, 50 * K);
       final String ONEHUNDREDK_STR = makeRoughly(ASCII, 100 * K);
       testPerformanceOfWriteToJspWriter(new TextSource()
       {
           public String getDesc()
           {
               return "With < than 8K of data";
           }
           public String getText(final int times)
           {
               return ASCII_TEXT;
           }
       });
       testPerformanceOfWriteToJspWriter(new TextSource()
       {
           public String getDesc()
           {
               return "With > than 8K of data";
           }
           public String getText(final int times)
           {
               return NINEK_STR;
           }
       });
       testPerformanceOfWriteToJspWriter(new TextSource()
       {
           public String getDesc()
           {
               return "With a 2/3 mix of small data and 1/3 > 8K of data";
           }
           public String getText(final int times)
           {
               if (times % 3 == 0)
               {
                   return NINEK_STR;
               }
               return ASCII_TEXT;
           }
       });
       testPerformanceOfWriteToJspWriter(new TextSource()
       {
           public String getDesc()
           {
               return "With a 1/2 mix of small data and 1/2 > 8K of data";
           }
           public String getText(final int times)
           {
               if (times % 2 == 0)
               {
                   return NINEK_STR;
               }
               return ASCII_TEXT;
           }
       });
       testPerformanceOfWriteToJspWriter(new TextSource()
       {
           public String getDesc()
           {
               return "With 50K of data";
           }
           public String getText(final int times)
           {
               return FIFTYK_STR;
           }
       });
       testPerformanceOfWriteToJspWriter(new TextSource()
       {
           public String getDesc()
           {
               return "With 100K of data";
           }
           public String getText(final int times)
           {
               return ONEHUNDREDK_STR;
           }
       });
   }
   
   public void testPerformanceOfWriteToJspWriter(TextSource textSource) throws IOException
   {
       NoopJspWriter noopJspWriter = new NoopJspWriter();
       String[] methods = {
               "writeTo (using hueristics)",
               "writeToViaSmoosh",
       };
       System.out.println(textSource.getDesc());
       System.out.println();
       float bestTime = Float.MAX_VALUE;
       String bestMethod = methods[0];
       for (int methodIndex = 0; methodIndex < methods.length; methodIndex++)
       {
           String method = methods[methodIndex];
           float totalTime = 0;
           final int MAX_TIMES = 10;
           final int MAX_ITERATIONS = 100;
           for (int times = 0; times < MAX_TIMES; times++)
           {
               String srcText = textSource.getText(times);
               for (int i = 0; i < MAX_ITERATIONS; i++)
               {
                   FastByteArrayOutputStream bout = new FastByteArrayOutputStream();
                   bout.write(srcText.getBytes(UTF_8));
                   // just time the JspWriter output. And let it warm u first as well
                   if (times > 3)
                   {
                       long then = System.currentTimeMillis();
                       switch (methodIndex)
                       {
                           case 0:
                               bout.writeTo(noopJspWriter, UTF_8);
                               break;
                           case 1:
                               bout.writeToViaSmoosh(noopJspWriter, UTF_8);
                               break;
                       }
                       long now = System.currentTimeMillis();
                       totalTime += (now - then);
                   }
               }
           }
           float avgTime = totalTime / MAX_TIMES / MAX_ITERATIONS;
           System.out.println(method + "  - Total Call Time = " + totalTime + "ms");
           System.out.println(method + " - Average Call Time = " + avgTime + "ms");
           System.out.println();
           if (avgTime < bestTime) {
               bestTime = avgTime;
               bestMethod = method;
           }
       }
       System.out.println(bestMethod + " was the best method - Average Call Time = " + bestTime + "ms");
       System.out.println("____________________\n");
   }
   interface TextSource
   {
       String getDesc();
       String getText(int times);
   }
   static class StringCapturingJspWriter extends NoopJspWriter
   {
       StringCapturingJspWriter()
       {
           super(true);
       }
   }
   static class NoopJspWriter extends JspWriter
   {
       final StringBuffer sb = new StringBuffer();
       final boolean capture;
       NoopJspWriter()
       {
           this(false);
       }
       NoopJspWriter(boolean capture)
       {
           super(0, false);
           this.capture = capture;
       }
       NoopJspWriter(final int i, final boolean b)
       {
           super(i, b);
           this.capture = false;
       }
       public String toString()
       {
           return sb.toString();
       }
       public void clear() throws IOException
       {
       }
       public void clearBuffer() throws IOException
       {
       }
       public void close() throws IOException
       {
       }
       public void flush() throws IOException
       {
       }
       public int getRemaining()
       {
           return 0;
       }
       public void newLine() throws IOException
       {
       }
       public void print(final char c) throws IOException
       {
           if (capture)
           {
               sb.append(c);
           }
       }
       public void print(final double v) throws IOException
       {
           if (capture)
           {
               sb.append(v);
           }
       }
       public void print(final float v) throws IOException
       {
           if (capture)
           {
               sb.append(v);
           }
       }
       public void print(final int i) throws IOException
       {
           if (capture)
           {
               sb.append(i);
           }
       }
       public void print(final long l) throws IOException
       {
           if (capture)
           {
               sb.append(l);
           }
       }
       public void print(final Object o) throws IOException
       {
           if (capture)
           {
               sb.append(o);
           }
       }
       public void print(final String s) throws IOException
       {
           if (capture)
           {
               sb.append(s);
           }
       }
       public void print(final boolean b) throws IOException
       {
           if (capture)
           {
               sb.append(b);
           }
       }
       public void print(final char[] chars) throws IOException
       {
           if (capture)
           {
               sb.append(chars);
           }
       }
       public void println() throws IOException
       {
           print("\n");
       }
       public void println(final char c) throws IOException
       {
           print(c);
           println();
       }
       public void println(final double v) throws IOException
       {
           print(v);
           println();
       }
       public void println(final float v) throws IOException
       {
           print(v);
           println();
       }
       public void println(final int i) throws IOException
       {
           print(i);
           println();
       }
       public void println(final long l) throws IOException
       {
           print(l);
           println();
       }
       public void println(final Object o) throws IOException
       {
           print(o);
           println();
       }
       public void println(final String s) throws IOException
       {
           print(s);
           println();
       }
       public void println(final boolean b) throws IOException
       {
           print(b);
           println();
       }
       public void println(final char[] chars) throws IOException
       {
           print(chars);
           println();
       }
       public void write(final char cbuf[], final int off, final int len) throws IOException
       {
           String s = new String(cbuf, off, len);
           print(s);
       }
   }

}</source>





ByteArrayOutputStream Demo

   <source lang="java">

import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; public class ByteArrayIOApp {

 public static void main(String args[]) throws IOException {
   ByteArrayOutputStream outStream = new ByteArrayOutputStream();
   String s = "This is a test.";
   for (int i = 0; i < s.length(); ++i)
     outStream.write(s.charAt(i));
   System.out.println("outstream: " + outStream);
   System.out.println("size: " + outStream.size());
   ByteArrayInputStream inStream;
   inStream = new ByteArrayInputStream(outStream.toByteArray());
   int inBytes = inStream.available();
   System.out.println("inStream has " + inBytes + " available bytes");
   byte inBuf[] = new byte[inBytes];
   int bytesRead = inStream.read(inBuf, 0, inBytes);
   System.out.println(bytesRead + " bytes were read");
   System.out.println("They are: " + new String(inBuf));
 }

}</source>