A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

本帖最后由 djbcool 于 2015-2-27 10:51 编辑

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

/*
* 需求:拷贝mp3文件,并比较字节流与缓冲字节流操作文件的效率。
*/
public class CopyMp3 {
        public static void main(String[] args) {
               
                long start = System.currentTimeMillis();
                noBuffered();
                long end = System.currentTimeMillis();
               
                System.out.println("未加入缓冲区: " + (end - start) + "毫秒");
               
                start = System.currentTimeMillis();
                buffered();
                end = System.currentTimeMillis();
               
                System.out.println("加入缓冲区: " + (end - start) + "毫秒");
        }
        
        //未加入缓冲区
        public static void noBuffered() {
                FileInputStream fis = null;
                FileOutputStream fos = null;
                try {
                        fis = new FileInputStream("d:\\1.mp3");
                        fos = new FileOutputStream("d:\\2.mp3");
               
                        byte[] buf = new byte[fis.available()];    //FileInputStream的available方法通过文件描述符获取文件的总大小
               
                        int len = 0;
                        
                        while ((len = fis.read(buf)) != -1) {
                                fos.write(buf, 0, len);
                        }
                } catch (IOException e) {
                        throw new RuntimeException("复制文件失败");
                } finally {
                        try {
                                fos.close();
                        } catch (IOException e){
                                throw new RuntimeException("关闭输出流失败");
                        }
                        try {
                                fis.close();
                        } catch (IOException e) {
                                throw new RuntimeException("关闭写入流失败");
                        }
                }        
        }
        
        //加入缓冲区
        public static void buffered() {
                BufferedInputStream bufis = null;
                BufferedOutputStream bufos = null;
               
                try {
                        bufis = new BufferedInputStream(new FileInputStream("d:\\3.mp3"));
                        bufos = new BufferedOutputStream(new FileOutputStream("d:\\4.mp3"));
                        for (int len = 0; (len = bufis.read()) != -1; ) {                        
                                bufos.write(len);
                        }
                        
                } catch (IOException e) {
                        throw new RuntimeException("复制文件失败");
                } finally {
                        try {
                                bufos.close();
                                
                        } catch (IOException e) {
                                throw new RuntimeException("关闭输出流失败");
                        } catch (NullPointerException e) {
                                throw new RuntimeException("找不到文件");
                        }
                        try {
                                bufis.close();
                                
                        } catch (IOException e) {
                                throw new RuntimeException("关闭写入流失败");
                        }
                }
        }
}

评分

参与人数 1技术分 +1 收起 理由
万合天宜 + 1 加油~

查看全部评分

15 个回复

倒序浏览

问题解决

问题:为什么BufferedInputStream比没加入缓冲区的FileInputStream拷贝文件速度还要慢?

解答:经过查询JDK1.6源码,发现BufferedInputStream使用的read方法是线程同步的。线程同步比非线程同步效率略低些。

  1. public synchronized int read() throws IOException {
  2.         if (pos >= count) {
  3.             fill();
  4.             if (pos >= count)
  5.                 return -1;
  6.         }
  7.         return getBufIfOpen()[pos++] & 0xff;
  8.     }
复制代码
回复 使用道具 举报
本帖最后由 sofeel 于 2015-2-26 10:47 编辑

缓冲技术就是提速的。
你的比较方式不公平:
1,对文件流的读写都用到了字节数组;你的核心代码:
  1. fis = new FileInputStream("d:\\1.mp3");
  2.                         fos = new FileOutputStream("d:\\2.mp3");
  3.                
  4.                         byte[] buf = new byte[fis.available()];   
  5.                
  6.                         int len = 0;
  7.                         
  8.                         while ((len = fis.read(buf)) != -1) {
  9.                                 fos.write(buf, 0, len);
  10.                         }
复制代码
2,对缓冲流却是直接读写的单个字节。
你的核心代码:

  1.                         bufis = new BufferedInputStream(new FileInputStream("d:\\3.mp3"));
  2.                         bufos = new BufferedOutputStream(new FileOutputStream("d:\\4.mp3"));
  3.                         for (int len = 0; (len = bufis.read()) != -1; ) {                        
  4.                                 bufos.write(len);
  5.                         }
复制代码
对第二点改进,再测。


评分

参与人数 1技术分 +1 收起 理由
万合天宜 + 1 很给力!

查看全部评分

回复 使用道具 举报
fantacyleo 来自手机 金牌黑马 2015-2-26 10:43:39
藤椅
比较快慢,1 要同一文件 不能一个用1.mp3另一个用3.mp3 2 要多次拷贝取平均值以减少无关因素干扰
回复 使用道具 举报
两个也没有差多少呀!应该考上百兆的东西试试,还有会用40多秒?是不是哪出错了?
回复 使用道具 举报
你让文件流 一筐一筐的搬东西,
却让缓冲流一个一个的搬东西,
这让缓冲流情何以堪!
回复 使用道具 举报 1 0
sofeel 发表于 2015-2-26 10:41
缓冲技术就是提速的。
你的比较方式不公平:
1,对文件流的读写都用到了字节数组;你的核心代码:

缓冲流是直接读写单个字节,多谢指点。我猜想,如果缓冲流内部封装的时候,直接读写字节数组而不是单个字节,效率不就更高咯?
回复 使用道具 举报
fantacyleo 发表于 2015-2-26 10:43
比较快慢,1 要同一文件 不能一个用1.mp3另一个用3.mp3 2 要多次拷贝取平均值以减少无关因素干扰 ...

不好意思同学,我之前没说清楚,其实1.mp3和3.mp3是同一个文件。
回复 使用道具 举报
Hsidar 发表于 2015-2-26 10:47
两个也没有差多少呀!应该考上百兆的东西试试,还有会用40多秒?是不是哪出错了? ...

感谢同学回复。这两个文件不是普通的mp3文件,而是一个1G左右的rar文件,更改后缀名为mp3,复制了成为了1.mp3和3.mp3。
回复 使用道具 举报
sofeel 中级黑马 2015-2-26 23:39:11
9#
本帖最后由 sofeel 于 2015-2-27 00:13 编辑

是的。
实际上 ,缓冲流内部就是带有这样的数组,才实现的缓冲效果。输入的内置数组一般需要指定大小,输出的内置数组一般是可变的。所以,用缓冲流一定用上它的数组,才有“快”感。
这些都是你提醒了我。
回复 使用道具 举报

第二次测试

我把之前测试拷贝的文件改成了一个10MB的mp3文件,把未加入缓冲区的输入流改成了按字节读入而不是按数组读入。结果如下:

未加入缓冲区: 45978毫秒
加入缓冲区: 81毫秒

结论是未加入缓冲区比加入缓冲区慢很多。但是问题又来了,既然未加入缓冲区的输入流按数组读入比加入缓冲区效率还高,为什么还需要缓冲区?
回复 使用道具 举报

第三次测试:问题依然存在

在第一次测试中,未缓冲的字节流是按数组读入,操作文件大小是1GB。在此基础上,让字节流缓冲区也按数组读入。代码如下:

  1. bufis = new BufferedInputStream(new FileInputStream("d:\\3.mp3"));
  2.                         bufos = new BufferedOutputStream(new FileOutputStream("d:\\4.mp3"));
  3.                        
  4.                         byte[] buf = new byte[bufis.available()];
  5.                         for (int len = 0; (len = bufis.read(buf)) != -1; ) {                       
  6.                                 bufos.write(buf, 0, len);
复制代码


运行结果:

未加入缓冲区: 40489毫秒
加入缓冲区: 42424毫秒

结论:加入缓冲区还是比未加入缓冲区耗时要长,说明字节流加入缓冲区读入数据并没有使效率提高。

问题:为什么会得出这样的结论?
回复 使用道具 举报
本帖最后由 sofeel 于 2015-2-27 00:28 编辑
djbcool 发表于 2015-2-27 00:05
在第一次测试中,未缓冲的字节流是按数组读入,操作文件大小是1GB。在此基础上,让字节流缓冲区也按数组读 ...

我才查了API.
字节缓冲输入流的read方法:
覆盖FilterInputStream 中的 read(不知道它是怎么覆盖的)

输出的write方法也是一样的。字节缓冲流感觉就是鸡li.

或许要看源码才能明白!呵呵,我没做好这个准备。

回复 使用道具 举报
本帖最后由 sofeel 于 2015-2-27 00:52 编辑
djbcool 发表于 2015-2-26 23:52
我把之前测试拷贝的文件改成了一个10MB的mp3文件,把未加入缓冲区的输入流改成了按字节读入而不是按数组读 ...
缓冲流应该是内置数组来实现缓冲的。用字节来读写,对于缓冲流而言,相当于先存入arr[0],再取出arr[0]。慢,应该是这个原因?
缓冲技术就是数组的缓冲。
用缓冲流,就用它的数组。你测试字节,这个方向感觉又偏了,这样的做法没意义啊
回复 使用道具 举报
sofeel 发表于 2015-2-27 00:33
缓冲流应该是内置数组来实现缓冲的。用字节来读写,对于缓冲流而言,相当于先存入arr[0],再取出arr[0]。慢 ...

缓冲流应该用的是内置数组,也许跟我们自定义数组的实现方式有所不同,具体的我也要去看看源码才明白。谢了!
回复 使用道具 举报
此外,经过查询源码。也证实了BufferedInputStream用的是字节数组。

  1. private void fill() throws IOException {
  2.         byte[] buffer = getBufIfOpen();
  3.         if (markpos < 0)
  4.             pos = 0;                /* no mark: throw away the buffer */
  5.         else if (pos >= buffer.length)        /* no room left in buffer */
  6.             if (markpos > 0) {        /* can throw away early part of the buffer */
  7.                 int sz = pos - markpos;
  8.                 System.arraycopy(buffer, markpos, buffer, 0, sz);
  9.                 pos = sz;
  10.                 markpos = 0;
  11.             } else if (buffer.length >= marklimit) {
  12.                 markpos = -1;        /* buffer got too big, invalidate mark */
  13.                 pos = 0;        /* drop buffer contents */
  14.             } else {                /* grow buffer */
  15.                 int nsz = pos * 2;
  16.                 if (nsz > marklimit)
  17.                     nsz = marklimit;
  18.                 byte nbuf[] = new byte[nsz];
  19.                 System.arraycopy(buffer, 0, nbuf, 0, pos);
  20.                 if (!bufUpdater.compareAndSet(this, buffer, nbuf)) {
  21.                     // Can't replace buf if there was an async close.
  22.                     // Note: This would need to be changed if fill()
  23.                     // is ever made accessible to multiple threads.
  24.                     // But for now, the only way CAS can fail is via close.
  25.                     // assert buf == null;
  26.                     throw new IOException("Stream closed");
  27.                 }
  28.                 buffer = nbuf;
  29.             }
  30.         count = pos;
  31.         int n = getInIfOpen().read(buffer, pos, buffer.length - pos);
  32.         if (n > 0)
  33.             count = n + pos;
  34.     }
复制代码
回复 使用道具 举报
您需要登录后才可以回帖 登录 | 加入黑马