FileChannel是什么

它是用于读取、写入、映射和操作文件的通道。除了熟悉的字节通道读取,写入和关闭操作之外,此类还定义了以下特定于文件的操作:

  • 可以以不影响通道当前位置的方式在文件中的绝对位置读取或写入字节。
  • 文件的区域可以直接映射到内存中。 对于大文件,这通常比调用通常的读取或写入方法要有效得多。
  • 对文件所做的更新可能会被强制发送到基础存储设备,以确保在系统崩溃时不会丢失数据。
  • 字节可以从文件传输到其他通道,反之亦然,可以通过许多操作系统进行优化,将字节快速传输到文件系统缓存或直接从文件系统缓存传输。
  • 文件的区域可能被锁定,以防止其他程序访问。

FileChannel配合着ByteBuffer,将读写的数据缓存到内存中,然后以批量/缓存的方式read/write,省去了非批量操作时的重复中间操作,操纵大文件时可以显著提高效率。ByteBuffer可以使用直接内存(系统内存)(allocateDirect),使用后无需jvm回收。

总结一下,按照字节读取,对大文件读取效率更高,无法设置为非阻塞模式,它总是运行在阻塞模式下。

但有时候我们要按照行读取文件,而FileChannel只能按照字节读取,所以这里需要对换行进行判断一下,在这里我对其进行了实现,供大家参考。

实现

import java.io.*;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;

public class test1 {
    public static void readLineByChannel(String path) throws IOException {
        long lineNumber = 0;
        FileInputStream fileIn = new FileInputStream(path);
        FileChannel fileChannel = fileIn.getChannel();
        // 开始按行读取
        int bufferSize = 1024 * 1024;  // 每一块的大小
        ByteBuffer buffer = ByteBuffer.allocate(bufferSize);
        byte b;

        while(fileChannel.read(buffer) > 0)
        {
            buffer.flip();
            for (int i = 0; i < buffer.limit(); i++)
            {
                b = buffer.get();
                if(b==10){  // 如果遇到换行
                    lineNumber++;
                }

            }
            buffer.clear(); // 清空buffer
        }
        fileChannel.close();
        System.out.println(lineNumber);
    }

    public static void readLineByBufferedReader(String path) throws IOException {
        long lineNumber = 0;
        FileInputStream inputStream = new FileInputStream(path);
        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
        String line;
        while((line=bufferedReader.readLine()) != null)
        {
            lineNumber++;
        }
        inputStream.close();
        bufferedReader.close();

        System.out.println(lineNumber);
    }

    public static void main(String[] args) throws IOException {
        String path = "大文件";
        long startTime = System.currentTimeMillis();
        readLineByChannel(path);
        System.out.println("readLineByChannel耗时:" + (System.currentTimeMillis() - startTime));
        startTime = System.currentTimeMillis();
        readLineByBufferedReader(path);
        System.out.println("readLineByBufferedReader耗时:" + (System.currentTimeMillis() - startTime));
    }
}

使用FileChannelBufferedReader分别的对大文件进行读取,并且计算有多少行。

//第一次测试:
169860474
readLineByChannel耗时:27310
169860474
readLineByBufferedReader耗时:24944
    
//第二次测试
169860474
readLineByChannel耗时:28677
169860474
readLineByBufferedReader耗时:21229

测试文件12GB,可以看出文件有1亿6千多万行,实际测试下来两者差距不大,甚至BufferedReader还快点。