几种Java读写数据的流性能对比

最近关乎性能问题,网上查了查资料,这里记录一下。

 

近来,在做服务器后台处理数据的时候,需要用到Java自带的几种流对数据进行读写,初始时没怎么在意,就随便用了一个,结果发现性能上并不尽如人意。于是对几种常用的流做了个小小的性能测试。测试代码如下:

  1     public static int FileOutputStreamTime = 0;
  2     public static int BufferedOutputStreamTime = 0;
  3     public static int FileWriterTime = 0;
  4     public static int FileInputStreamTime = 0;
  5     public static int BufferedInputStreamTime = 0;
  6     public static int FileReaderTime = 0;
  7     
  8     public static void write(String filePath, String content){
  9         FileOutputStream out = null;
 10         FileOutputStream outStr = null;
 11         BufferedOutputStream buf = null;
 12         FileWriter fw = null;
 13         File f = new File(filePath);
 14         
 15         try {
 16             //Test FileOutputStream
 17             long begin1 = System.currentTimeMillis();            
 18             out = new FileOutputStream(f);
 19             out.write(content.getBytes());
 20             out.close();
 21             long end1 = System.currentTimeMillis();
 22             FileOutputStreamTime += end1 - begin1;
 23 
 24             //Test BufferedOutputStream
 25             long begin2 = System.currentTimeMillis();
 26             outStr = new FileOutputStream(f);
 27             buf = new BufferedOutputStream(outStr);
 28             buf.write(content.getBytes());
 29             buf.flush();
 30             buf.close();
 31             long end2 = System.currentTimeMillis();
 32             BufferedOutputStreamTime += end2 - begin2;
 33 
 34             //Test FileWriter
 35             long begin3 = System.currentTimeMillis();
 36             // the second parameter "true",Whether or not a file will be covered
 37             // or appended.
 38             fw = new FileWriter(f);
 39             // fw = new FileWriter("d:/test/testwrite/add2.txt",true);
 40             fw.write(content);
 41             fw.close();
 42             long end3 = System.currentTimeMillis();
 43             FileWriterTime += end3 - begin3;
 44         } catch (Exception e) {
 45             e.printStackTrace();
 46         } finally {
 47             try {
 48                 fw.close();
 49                 buf.close();
 50                 outStr.close();
 51                 out.close();
 52             } catch (Exception e) {
 53                 e.printStackTrace();
 54             }
 55         }
 56     }
 57     
 58     public static void read(String filePath){
 59         FileInputStream in = null;
 60         BufferedInputStream buf = null;
 61         FileReader reader = null;
 62         BufferedReader br = null;
 63         StringBuffer sb = new StringBuffer();
 64         
 65         try {
 66             //Test FileInputStream
 67             long begin1 = System.currentTimeMillis();
 68             File f = new File(filePath);
 69             in = new FileInputStream(f);
 70             int len1 = 512;
 71             byte[] bytes1 = new byte[len1];
 72             
 73             while ((len1 = in.read(bytes1, 0, len1)) != -1) {
 74                 if(len1 < 512){
 75                     byte[] tmpBuf = new byte[len1];
 76                     System.arraycopy(bytes1, 0, tmpBuf, 0, len1);                
 77                     sb.append(new String(tmpBuf));
 78                     tmpBuf = null;
 79                 }else{
 80                     sb.append(new String(bytes1));
 81                 }
 82             }
 83 
 84             in.close();
 85             long end1 = System.currentTimeMillis();
 86             FileInputStreamTime += end1 - begin1;
 87             
 88             //Test BufferedInputStream
 89             long begin2 = System.currentTimeMillis();
 90             int len2 = 512;
 91             byte[] bytes2 = new byte[len2];
 92             buf = new BufferedInputStream(new FileInputStream(f));
 93             while ((len2 = buf.read(bytes2, 0, len2)) != -1) {
 94                 if(len2 < 512){
 95                     byte[] tmpBuf = new byte[len2];
 96                     System.arraycopy(bytes2, 0, tmpBuf, 0, len2);                
 97                     sb.append(new String(tmpBuf));
 98                     tmpBuf = null;
 99                 }else{
100                     sb.append(new String(bytes2));
101                 }
102             }
103 
104             buf.close();
105             long end2 = System.currentTimeMillis();
106             BufferedInputStreamTime += end2 - begin2;
107             
108             //Test FileReader
109             long begin3 = System.currentTimeMillis();
110             reader = new FileReader(f);
111             br = new BufferedReader(reader);
112             String str;
113             while ((str = br.readLine()) != null) {
114                 sb.append(str);
115             }
116             br.close();
117             reader.close();
118             long end3 = System.currentTimeMillis();
119             FileReaderTime += end3 - begin3;
120 
121         } catch (Exception e) {
122             e.printStackTrace();
123         } finally {
124             try {
125                 br.close();
126                 reader.close();
127                 in.close();
128                 buf.close();
129             } catch (Exception e) {
130                 e.printStackTrace();
131             }
132         }
133     }

 

  测试时,分别对不同大小的数据做500次同样的操作,取得的平均耗时如下:

不同流耗时对比(ms)
 10K100K200K400K
FileOutputStream0.4960.7941.0781.602
BufferedOutputStream0.3860.6840.8781.246
FileWriter0.5860.9961.31.946
FileInputStream0.1580.5440.9841.876
BufferedInputStream0.1520.3960.6681.068
FileReader0.1520.6081.2542.19

  通过测试,可以发现,就写数据而言,BufferedOutputStream耗时是最短的,而性能FileWriter最差;读数据方面,BufferedInputStream性能最好,而FileReader性能最差劲。所以,以后在涉及到流的读写方面时,要注意一下了~

  FileOutputStream 用于写入诸如图像数据之类的原始字节的流。要写入字符流,请考虑使用 FileWriter。

  BufferedOutputStream的数据成员buf是一个位数组,默认为512字节。当使用write()方法写入数据时,实际上会先将数据写至buf中,当buf已满时才会实现给定的OutputStream对象的write()方法,将buf数据写至目的地,而不是每次都对目的地作写入的动作。BufferedInputStream也是类似。

  另外,有时只是要存储一个对象的成员数据,而不是整个对象的信息,成员数据的类型假设都是Java的基本数据类型,这样的需求不必要使用到与Object输入、输出相关的流对象,可以使用DataInputStream、DataOutputStream来写入或读出数据。在从文件中读出数据时,不用费心地自行判断读入字符串时或读入int类型时何时该停止,使用对应的readUTF()或readInt()方法就可以正确地读入完整类型数据。

  在Java程序执行的过程中,很多数据都是以对象的方式存在于内存中。有时会希望直接将内存中整个对象存储至文件,而不是只存储对象中的某些基本类型成员信息,而在下一次程序运行时,希望可以从文件中读出数据并还原为对象。这时可以使用java.io.ObjectInputStream和java.io.ObjectOutputStream来进行这项工作。

  ByteArrayInputStream可以将一个数组当作流输入的来源,而ByteArrayOutputStream则可以将一个位数组当作流输出的目的地。在这里举一个简单的文件位编辑程序作为例子,您可以打开一个简单的文本文件,其中有简单的A、B、C、D、E、F、G等字符,在读取文件之后,可以直接以程序来指定文件中位的位置来修改所指定的字符。作法是将文件读入数组中,指定数组索引修改元素,然后重新将数组存回文件。

  java.io.Reader和java.io.Writer支持Unicode标准字符集(Character Set)(字节流则只支持ISO-Latin-1 8-bit)。在处理流数据时,会根据系统默认的字符编码来进行字符转换,Reader和Writer是抽象类,在进行文本文件的字符读写时真正会使用其子类,子类通常会重新定义相关的方法。

  如果想要存取的是一个文本文件,可以直接使用java.io.FileReader和java.io.FileWriter类,它们分别继承自InputStreamReader与OutputStreamWriter。可以直接指定文件名称或File对象来打开指定的文本文件,并读入流转换后的字符,字符的转换会根据系统默认的编码(若要指定编码,则还是使用InputStreamReader与OutputStreamWriter)。

  java.io.BufferedReader与java.io.BufferedWriter类各拥有8192字符的缓冲区。当BufferedReader在读取文本文件时,会先尽量从文件中读入字符数据并置入缓冲区,而之后若使用read()方法,会先从缓冲区中进行读取。如果缓冲区数据不足,才会再从文件中读取,使用BufferedWriter时,写入的数据并不会先输出至目的地,而是先存储至缓冲区中。如果缓冲区中的数据满了,才会一次对目的地进行写出。例如一个文件,通过缓冲区可减少对硬盘的输入/输出动作,以提高文件存取的效率。

 

BufferedOutputStream和ByteArrayOutputStream区别

今天主要是想记录下BufferedOutputStream和ByteArrayOutputStream区别

众所周知BufferedOutputStream是一个缓冲数据输出流接口, ByteArrayOutputStream则是字节数组输出流接口. 这2个输出流都是我们经常用到的, 它们都是OutputStream的子类,而什么时候选择用它们呢, 这个就要看你运用到什么应用场景下了.

下来先来看下源码吧.

1.BufferedOutputStream会首先创建一个默认的容器量, capacity = 8192 = 8KB, 每次在写的时候都会去比对capacity是否还够用, 如果不够用的时候, 就flushBuffer(), 把buf中的数据写入对应的outputStream中, 然后将buf清空, 一直这样等到把内容写完. 在这过程中主要起到了一个数据缓冲的功能.

  1. public synchronized void write(byte b[], int off, int len) throws IOException {  
  2.       // 在这判断需要写的数据长度是否已经超出容器的长度了,如果超出则直接写到相应的outputStream中,并清空缓冲区  
  3.       if (len >= buf.length) {  
  4.           flushBuffer();  
  5.           out.write(b, off, len);  
  6.           return;  
  7.       }  
  8.       // 判断缓冲区剩余的容量是否还够写入当前len的内容,如果不够则清空缓冲区  
  9.       if (len > buf.length - count) {  
  10.           flushBuffer();  
  11.       }  
  12.       // 将要写的数据先放入内存中,等待数据达到了缓冲区的长度后,再写到相应的outputStream中  
  13.       System.arraycopy(b, off, buf, count, len);  
  14.       count += len;  
  15.     }  



flushBuffer () 这个方法干了些什么呢, 来看看源码

  1. private void flushBuffer() throws IOException {  
  2.        if (count > 0) {  
  3.           // 把写入内存中的数据写到构造方法里传入的OutputStream句柄里, 并把容量大小清楚  
  4.     out.write(buf, 0, count);  
  5.     count = 0;  
  6.        }  
  7.    }  


这个类最重要的就是这2个方法, 这样节省了大量的内存空间, 合理的分配内存来完成数据输出,当你资源不是那么充沛时, 选择这个类来实现你想要的东西是不是很合适呢?

2.普通的OutputStream, 例如ByteArrayOutputStream也会首先创建一个默认的容器量, capacity = 32 = 32byte, 每次在写的时候都会去比对capacity是否还够用, 如果不够用的时候, 就重新创建buf的容量, 一直等到内容写完, 这些数据都会一直处于内存中.

 

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

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


  /**
     * Increases the capacity to ensure that it can hold at least the
     * number of elements specified by the minimum capacity argument.
     *
     * @param minCapacity the desired minimum capacity
     */
    private void grow(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = buf.length;
        int newCapacity = oldCapacity << 1;
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        buf = Arrays.copyOf(buf, newCapacity);
    }


   /**
     * Increases the capacity if necessary to ensure that it can hold
     * at least the number of elements specified by the minimum
     * capacity argument.
     *
     * @param minCapacity the desired minimum capacity
     * @throws OutOfMemoryError if {@code minCapacity < 0}.  This is
     * interpreted as a request for the unsatisfiably large capacity
     * {@code (long) Integer.MAX_VALUE + (minCapacity - Integer.MAX_VALUE)}.
     */
    private void ensureCapacity(int minCapacity) {
        // overflow-conscious code
        if (minCapacity - buf.length > 0)
            grow(minCapacity);
    }



   /**
     * Writes <code>len</code> bytes from the specified byte array
     * starting at offset <code>off</code> to this byte array output stream.
     *
     * @param   b     the data.
     * @param   off   the start offset in the data.
     * @param   len   the number of bytes to write.
     */
    public synchronized void write(byte b[], int off, int len) {
        if ((off < 0) || (off > b.length) || (len < 0) ||
            ((off + len) - b.length > 0)) {
            throw new IndexOutOfBoundsException();
        }
        ensureCapacity(count + len);
        System.arraycopy(b, off, buf, count, len);
        count += len;
    }


注释中已经对这个类进行了详细的解释

总结 :

当你资源不足够用时,选择BufferedOutputStream是最佳的选择,

当你选择快速完成一个作业时,可以选择ByteArrayOutputStream之类的输出流

 

转自:

https://www.cnblogs.com/Scott007/archive/2013/05/14/3078759.html

https://www.iteye.com/blog/z276356445t-1955400

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值