Chinaunix首页 | 论坛 | 博客
  • 博客访问: 465087
  • 博文数量: 145
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 1060
  • 用 户 组: 普通用户
  • 注册时间: 2013-08-22 11:52
个人简介

专注计算机技术: Linux Android 云计算 虚拟化 网络

文章分类

全部博文(145)

文章存档

2016年(3)

2015年(21)

2014年(75)

2013年(46)

我的朋友

分类: Java

2014-01-28 11:05:18

BufferedInputStream和GZIPInputStream都是用来从文件中读取数据,缓存区读取文件数据是一种优化的读取文件的方式。不过这里仍然有些问题需要理清楚
什么时候不需要缓存读取
当我们需要从输入设备读取数据时,设置缓存区读取用来减少单独读取的次数,批量一次性读取多个字节数据,而不是一个一个字节读取。 通过使用方式如下
final InputStream is = new BufferedInputStream( new FileInputStream( file ) );
当你的数据块的大小设置得足够大,比如100k,然后这个块能够装入你需要的数据内容,则不需要缓存区,否则都需要设置缓存区输入流。
比如下面的文件copy操作不需要设置缓存区
public static void copyFile( final File from, final File to ) throws IOException { 
    final InputStream is = new FileInputStream( from ); 
    try 
    { 
        final OutputStream os = new FileOutputStream( to ); 
        try 
        { 
            final byte[] buf = new byte[ 8192 ]; 
            int read = 0; 
            while ( ( read = is.read( buf ) ) != -1 ) 
            { 
                os.write( buf, 0, read ); 
            } 
        } 
        finally { 
            os.close(); 
        } 
    } 
    finally { 
        is.close(); 
    } 
}
  
不过这种方式很难测试文件拷贝的性能,依赖操作系统的缓冲区设置
我们通常可以使用java 的nio的 FileChannel.transferTo 和 transferFrom 来完成文件拷贝,避免缓存区数据从内核空间到用户空间的双向拷贝,使用方式如下
private static void copyFileNio( final File from, final File to ) throws IOException { 
    final RandomAccessFile inFile = new RandomAccessFile( from, "r" ); 
    try 
    { 
        final RandomAccessFile outFile = new RandomAccessFile( to, "rw" ); 
        try 
        { 
            final FileChannel inChannel = inFile.getChannel(); 
            final FileChannel outChannel = outFile.getChannel(); 
            long pos = 0; 
            long toCopy = inFile.length(); 
            while ( toCopy > 0 ) 
            { 
                final long bytes = inChannel.transferTo( pos, toCopy, outChannel ); 
                pos += bytes; 
                toCopy -= bytes; 
            } 
        } 
        finally { 
            outFile.close(); 
        } 
    } 
    finally { 
        inFile.close(); 
    } 

BufferedInputStream 的默认缓冲区大小设置为8192字节,如果文件比较大,我们可以设置为64K (65536)来减少磁盘读写次数。不过磁盘一般块大小为4096 ,所以建议将缓存区大小也设置为4096 。
 GZIPInputStream 用来读取zip压缩文件的输入流
 final InputStream is = new GZIPInputStream( new BufferedInputStream( new FileInputStream( file ) ) );
 这样初始化没有问题,不过使用BufferedInputStream 是多余的,因为GZIPInputStream 有内置的缓存区设置,默认为512 字节,太小了我们可以设置为更大点的
 final InputStream is = new GZIPInputStream( new FileInputStream( file ), 65536 );
 BufferedInputStream.available 返回缓存区剩余数据大小加上从inputstream的available方法调用返回的值的和。其实很多情况下我们只是想查缓存区剩余的数据大小是不是大于0而已,如果是文件流可以通过FileInputStream.available().获取文件流中剩余的数据,这样BufferedInputStream 不是final类,我们可以覆盖available。在jdk6中如果缓存区剩余数据大小加上从inputstream的available方法调用返回的值的和大于Integer.MAX_VALUE,会返回负值。在java7已经修复这个问题。因为BufferedInputStream会没有数据时,会继续读取输入流的数据到缓冲区,所以不需要一直检测输入流剩余数据的大小来提高性能。
 下面是覆盖available来实现我们的功能
 public class FasterBufferedInputStream extends BufferedInputStream
 {
     public FasterBufferedInputStream(InputStream in, int size) {
         super(in, size);
     }
  
     //This method returns positive value if something is available, otherwise it will return zero.
     public int available() throws IOException {
         if (in == null)
             throw new IOException( "Stream closed" );
         final int n = count - pos;
         return n > 0 ? n : in.available();
     }
 }
阅读(1435) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~