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();
}
}
什么时候不需要缓存读取
当我们需要从输入设备读取数据时,设置缓存区读取用来减少单独读取的次数,批量一次性读取多个字节数据,而不是一个一个字节读取。 通过使用方式如下
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();
}
}