FileInputStream与BufferedInputStream

程序中有一块儿功能是读取硬盘文件到内存里。这块儿代码执行得异常缓慢,读4个G左右的文件竟会用上十几分钟。平时也不着急,但是今天催得紧,就得优化下了。

上传代码大致如下:

    public void readInByte(File file) throws IOException {
        FileInputStream input = new FileInputStream(file);
        int b = 0;
        // 每次循环读取一个字节并返回
        while ((b = input.read()) != -1) {
            // action with b
        }
    }

这里的问题在于直接使用了FileInputStream,就是说没有使用任何缓冲(buffer)。因此是一个字节一个字节地从磁盘里面读取文件,又一个字节一个字节地写到内存里,这样子效率可想而知。

尝试优化下,每次多读一些数据:

    public void readInArray(File file) throws IOException {
        FileInputStream input = new FileInputStream(file);
        int len = 0;
        byte[] arr = new byte[1024];
        // 将数据放到缓存数组中再返回
        while ((len = input.read(arr)) != -1) {
            byte[] bytes = new byte[len];
            System.arraycopy(arr, 0, bytes, 0, len);
            // action with bytes
        }
    }

这样效果明显好了些,因为不再是一个字节一个字节的读了,而是每次读取了1024个字节的数据,也就是每1024字节内存才会与硬盘进行一次交互。因为大量减少了硬盘与内存的交互,速度自然就快了。

继续优化,使用BufferedInputStream:

    public void readWithBuffer(File file) throws IOException {
        FileInputStream input = new FileInputStream(file);
        BufferedInputStream bis = new BufferedInputStream(input);
 
        int len = 0;
        byte[] buffer = new byte[1024];
        // 这里通过BufferedInputStream利用了缓冲区来提速
        while ((len = bis.read(buffer)) != -1) {
            byte[] bytes = new byte[len];
            System.arraycopy(buffer, 0, bytes, 0, len);
            // action with bytes
        }
    }

BufferedInputStream是FilterInputStream的子类,实现了装饰设计模式。BufferedInputStream是带缓冲区的输入流,默认缓冲区大小是8M,也就是说这个类会一次读取8M数据到内存缓存里以便进行后续操作。其原理与程序二差不多。

通过这三段程序分别测试读取了一个不太大的文件(1.57M),这三段程序的耗时分别是 8825、15、5 ,时间单位毫秒。

做一次调整:将程序二的缓存数组长度改为2048 * 1024,会看到执行时长也是 5ms

再做一次调整:这次读取一个 34.6M 的文件,并将程序二的缓存数组长度调整为8 * 1024 * 1024。可以看到程序二与程序三的执行时长分别是 73ms与97ms 。二者的性能还是比较接近的。

就这样!

我来评几句
登录后评论

已发表评论数()

相关站点

+订阅
热门文章