为什么Java的缓冲流在复制文本文档内容上消耗的时间比普通的输入输出流长呢?

同为复制这样的文本文件(如图)

img

编写的代码如下:


import java.io.*;
import java.util.Scanner;

/**
 * @author Maximilian_Li
 */
public class TestEfficiency {
    public static void main(String[] args) throws IOException {
        String basicFilePath = "D:\\Java\\Note&Code\\Java_Network_Programming\\src\\Chapter01_Stream\\" +
                "Section03_BufferedStream\\";

        String filePathFrom = basicFilePath + "from.txt";

        String filePathTo = basicFilePath + "to.txt";

        File fromFile = new File(filePathFrom);
        File toFile = new File(filePathTo);

        while(true) {
            Scanner sc = new Scanner(System.in);
            System.out.println("\r\n请输入你的选择(【1】代表普通输入输出流,【2】代表缓冲输入输出流)。\r\n");
            int anInt = sc.nextInt();

            switch (anInt) {
                case 1:
                    FileInputStream fromStream = new FileInputStream(fromFile);
                    FileOutputStream toStream = new FileOutputStream(toFile);

                    System.out.println("普通流所需时间  : " + timeOfOriginal(fromStream, toStream));

                    break;
                case 2:
                    BufferedReader buffFrom = new BufferedReader(new FileReader(fromFile));
                    BufferedWriter buffTo = new BufferedWriter(new FileWriter(toFile));

                    System.out.println("缓冲流所用时间  : " + timeOfBuffered(buffFrom, buffTo) + "\r\n");

                    break;
                default:
                    System.exit(0);
            }
        }
    }

    private static long timeOfBuffered(BufferedReader buffFrom, BufferedWriter buffTo) throws IOException {
        long startTime = System.currentTimeMillis();
        System.out.println(startTime);

        String aLine = null;
        while ((aLine = buffFrom.readLine()) != null) {
            buffTo.write(aLine);
            buffTo.newLine();
        }

        buffFrom.close();
        buffTo.close();

        long endTime = System.currentTimeMillis();
        System.out.println(endTime);

        return (endTime - startTime);
    }

    private static long timeOfOriginal(FileInputStream fromStream, FileOutputStream toStream) throws IOException {
        long startTime = System.currentTimeMillis();
        System.out.println(startTime);

        int length = 0;
        byte[] bytes = new byte[1024];

        while ((length = fromStream.read(bytes)) != -1) {
            toStream.write(bytes,0,length);
        }

        fromStream.close();
        toStream.close();

        long endTime = System.currentTimeMillis();
        System.out.println(endTime);

        return (endTime - startTime);
    }
}

输出结果为这样(如图)

img

但是据我了解,缓冲流的效率是远高于普通输入输出流的,为什么会出现这种状况呢?

嗯,之前没有细看,只是觉得应该是这个原因,我的意思你读的东西太少了:

  • 比如你用数据库连接池和直接用JDBC连据库连。理论上连接池更好,因为多个连接不用建立关闭连接,直接用池里的,数据库连接的建立和关闭是耗费系统资源的操作。(同样IO也是消耗资源的事),但是现实情况是你连接一次。那还不如用JDBC了,时间上来讲,你还没有构建完连接池数据源对象,可能jdbc已经连接完成了。所以我说你这个可能是数据太少。

看你回复了,我看了下代码:嗯,我没有那你的代码实际测试,下面是一些个人想法,你可以参考一下。

首先你的复制方式都是边写边读的方式,区别在于:

  • FileInputStramd相关使用的是 批量拷贝(循环读取,每次读入一个byte数组)
  • BufferedReader相关使用的是批量+缓冲拷贝(循环批量读取到字节数组中,然后使用缓冲输出流写入到文件)

但是这里有一个问题:你在使用BufferedWriter类的Writer方法时,数据并没有立刻写入输入流,而是首先进入缓存区中,如果想立刻缓存区中的数据写入输入流,一定要调用flush()方法。现在写到缓存区交给缓存流去写,如果数据很少的话,就没必要搞一个缓存流去写,太消耗时间了资源了。

生成一个大文件,用你的代码测试一下:


img


如果你每次都调flush()去刷新的话,可以看出 ,建立缓存区消耗的时间很多。

while ((aLine = buffFrom.readLine()) != null) {
            buffTo.write(aLine);
            buffTo.newLine();
            buffTo.flush();
        }

img


package com;

/**
 * @Classname TestEfficiency
 * @Description TODO
 * @Date 2021/8/14 10:40
 * @Created LiRuilong
 */

import java.io.*;
import java.util.Scanner;
/**
 * @author Maximilian_Li
 */
public class TestEfficiency {
    public static void main(String[] args) throws Exception {

        String basicFilePath = "";
        System.out.println(basicFilePath);
        String filePathFrom = basicFilePath + "from.txt";
        String filePathTo = basicFilePath + "to.txt";
        File fromFile = new File(filePathFrom);
        File toFile = new File(filePathTo);
        System.out.println(fromFile.getAbsolutePath());
       /* fromFile.createNewFile();
        FileOutputStream fileOutputStream = new FileOutputStream(fromFile,true);
        for (int i = 0; i < Integer.MAX_VALUE; i++) {
           fileOutputStream.write("请输入你的选择(【1】代表普通输入输出流,【2】代表缓冲输入输出流)。\n".getBytes());
        }*/


        while(true) {
            Scanner sc = new Scanner(System.in);
            System.out.println("\r\n请输入你的选择(【1】代表普通输入输出流,【2】代表缓冲输入输出流)。\r\n");
            int anInt = sc.nextInt();
            switch (anInt) {
                case 1:
                    FileInputStream fromStream = new FileInputStream(fromFile);
                    FileOutputStream toStream = new FileOutputStream(toFile);
                    System.out.println("普通流所需时间  : " + timeOfOriginal(fromStream, toStream));
                    break;
                case 2:
                    BufferedReader buffFrom = new BufferedReader(new FileReader(fromFile));
                    BufferedWriter buffTo = new BufferedWriter(new FileWriter(toFile));
                    System.out.println("缓冲流所用时间  : " + timeOfBuffered(buffFrom, buffTo) + "\r\n");
                    break;
                default:
                    System.exit(0);
            }
        }
    }
    private static long timeOfBuffered(BufferedReader buffFrom, BufferedWriter buffTo) throws IOException {
        long startTime = System.currentTimeMillis();
        System.out.println(startTime);
        String aLine = null;
        buffFrom.close();
        buffTo.close();
        long endTime = System.currentTimeMillis();
        System.out.println(endTime);
        return (endTime - startTime);
    }
    private static long timeOfOriginal(FileInputStream fromStream, FileOutputStream toStream) throws IOException {
        long startTime = System.currentTimeMillis();
        System.out.println(startTime);
        int length = 0;
        byte[] bytes = new byte[1024];
        while ((length = fromStream.read(bytes)) != -1) {
            toStream.write(bytes,0,length);
        }
        fromStream.close();
        toStream.close();
        long endTime = System.currentTimeMillis();
        System.out.println(endTime);
        return (endTime - startTime);
    }
}

嗯,可以你读的东西太少了