我正在开发一个Java类,用于将数据从任何输入流传输到任何输出流(主要用于在线,但具有更广泛的实用程序)。
在浏览Java 7 API规范后,我注意到FileChannel
类中的两种方法:transferFrom(...)
和transferTo
。
我围绕这些方法开发了我的类,并创建了以下transmit()
方法:
public void transmit() throws IOException {
File tmp = File.createTempFile("transmit", ".tmp");
RandomAccessFile raf = new RandomAccessFile(tmp, "rw");
FileChannel fc = raf.getChannel();
fc.force(true);
fc.transferFrom(Channels.newChannel(src), 0, Long.MAX_VALUE);
raf.seek(0);
fc.transferTo(0, Long.MAX_VALUE, Channels.newChannel(dst));
raf.close();
}
然后我决定用以下测试对它进行测试(第一个版本没有使用临时字节数组,并且正如预期的那样比transmit()
方法更差):
public static void main(String[] args) throws IOException {
File from = File.createTempFile("source", ".tmp");
File to = File.createTempFile("destination", ".tmp");
FileOutputStream tmp = new FileOutputStream(from);
for (int i = 0; i < (1 << 20); i++) {
tmp.write(0);
}
tmp.close();
FileInputStream fin = new FileInputStream(from);
FileOutputStream fout = new FileOutputStream(to);
DataTransmitter dt = new DataTransmitter(fin, fout);
long time_1 = new Date().getTime();
dt.transmit();
time_1 = new Date().getTime() - time_1;
fin.close();
fout.close();
to.delete();
fin = new FileInputStream(from);
fout = new FileOutputStream(to);
int len;
byte[] b = new byte[8192];
long time_2 = new Date().getTime();
while ((len = fin.read(b)) >= 0) {
fout.write(b, 0, len);
}
time_2 = new Date().getTime() - time_2;
fin.close();
fout.close();
System.out.format("Transmitter method: %s milliseconds%n", time_1);
System.out.format("Direct method: %s milliseconds%n", time_2);
}
在位于本地硬盘驱动器上的文件中,所谓的“直接方法”非常快(transmit()
方法为192毫秒,“直接方法”为8毫秒),这意味着“直接方法”是与transmit()
方法相比效率很高。
但是,由于此类的目的是从在线源下载文件,我创建了一个40 Mb文件,我上传到个人云(并从类似于上面的方法)得到以下结果:< / p>
Transmitter method: 126478 milliseconds
Direct method (8192 bytes): 134105 milliseconds
这意味着来自在线资源的transmit()
方法效率更高。
我想知道这些结果是否准确(因为这些方法是系统相关的,我说我正在运行Ubuntu 13.10),如果它们是,我怎样才能找到一种方法来优化transmit()
方法到一般情况。
如果有更高效的替代方法,我也会感激,如果有人说出来的话(不需要代码,只是对方法的解释,也许是为什么它更有效)。
答案 0 :(得分:2)
除非您有非常具体的理由,否则请勿编写自己的数据传输代码。有许多数据传输java库:IOUtils,Apache Mina的某些部分,等等。你不应该重新发明轮子,除非你想为学校项目做这件事,或者你看到现有的弱点你想要改进的解决方案。
另外,谈到不好的做法,你不应该使用java的Date,而是使用更加理智的Date功能,比如Joda's DateTime,你不应该打开/关闭自己的流,而是使用库这样做或者使用Java 7的Closable和try-with-resources语句。
修改强> 您在示例中执行的另一个不好的做法是使用java.util.Date来获取当前时间。这在两个层面上是错误的:代码可读性和线程安全性。
对于代码可读性部分:您要做的是'give me the current time'
,您可以通过System.getCurrentTimeMillis()
明确地执行此操作。你实际在做的是:
'分配一个半弃用的对象,不要指定时区或语言环境 (可能会从第一次呼叫变为第二次呼叫),不要 线程安全,并给我它当前的时间,所以我可以用它作为我的 当前时间'
。当你可以轻松地避免它时创建两个额外的对象并不是什么大问题,但是因为存在一种给你当前时间的方法,而且效率更高,你应该使用它,否则你就会开发不好的做法。
对于线程安全部分,您可以阅读: How to make Java.util.Date thread-safe
使用java.util.Date的代码本质上容易出错,你不应该使用它。在这个例子中,这似乎不是什么大不了的事,但如果你是一名软件工程师,那么细节就很重要。此外,您不应该编写自己的数据传输方法,除非您知道现有的方法有什么问题,并且您想要改进或者您正在为家庭作业或学习目的这样做。