
早些时候曾提到从文件里面读取字符的方法调用的消耗可能是重大的。这个问题在计算文本文件的行数的另一个例子中也可以找到。:
import java.io.*;
public class line1 {
public static void main(String args[]) {
if (args.length != 1) {
System.err.println("missing filename");
System.exit(1);
}
try {
FileInputStream fis = new FileInputStream(args[0]);
BufferedInputStream bis = new BufferedInputStream(fis);
DataInputStream dis = new DataInputStream(bis);
int cnt = 0;
while (dis.readLine() != null)
cnt++;
dis.close();
System.out.println(cnt);
} catch (IOException e) {
System.err.println(e);
}
}
}这个程序使用老的DataInputStream.readLine 方法,该方法是使用用读取每个字符的 read 方法实现的。一个新方法是:
import java.io.*;
public class line2 {
public static void main(String args[]) {
if (args.length != 1) {
System.err.println("missing filename");
System.exit(1);
}
try {
FileReader fr = new FileReader(args[0]);
BufferedReader br = new BufferedReader(fr);
int cnt = 0;
while (br.readLine() != null)
cnt++;
br.close();
System.out.println(cnt);
} catch (IOException e) {
System.err.println(e);
}
}
}这个方法更快。例如在一个有200,000行的 6 MB文本文件上,第二个程序比第一个快大约20%。
但是即使第二个程序不是更快的,第一个程序依然有一个重要的问题要注意。第一个程序在JavaTM 2编译器下引起了不赞成警告,因为DataInputStream.readLine太陈旧了。它不能恰当的将字节转换为字符,因此在操作包含非ASCII字符的文本文件时可能是不合适的选择。(Java语言使用Unicode字符集而不是ASCII)
这就是早些时候提到的字节流和字符流之间的区别。像这样的一个程序:
import java.io.*;
public class conv1 {
public static void main(String args[]) {
try {
FileOutputStream fos = new FileOutputStream("out1");
PrintStream ps = new PrintStream(fos);
ps.println("uffffu4321u1234");
ps.close();
} catch (IOException e) {
System.err.println(e);
}
}
}向一个文件里面写,但是没有保存实际的Unicode字符输出。Reader/Writer I/O 类是基于字符的,被设计用来解决这个问题。OutputStreamWriter 应用于字节编码的字符。
一个使用PrintWriter写入Unicode字符的程序是这样的:
import java.io.*;
public class conv2 {
public static void main(String args[]) {
try {
FileOutputStream fos = new FileOutputStream("out2");
OutputStreamWriter osw = new OutputStreamWriter(fos, "UTF8");
PrintWriter pw = new PrintWriter(osw);
pw.println("uffffu4321u1234");
pw.close();
} catch (IOException e) {
System.err.println(e);
}
}
}
这个程序使用UTF8编码,具有ASCII文本是本身而其他字符是两个或三个字节的特性。
格式化的代价
实际上向文件写数据只是输出代价的一部分。另一个可观的代价是数据格式化。考虑一个三部分程序,它像下面这样输出一行:
The square of 5 is 25
方法 1
第一种方法简单的输出一个固定的字符串,了解固有的I/O开销:
public class format1 {
public static void main(String args[]) {
final int COUNT = 25000;
for (int i = 1; i <= COUNT; i++) {
String s = "The square of 5 is 25n";
System.out.print(s);
}
}
}
方法2
第二种方法使用简单格式"+":
public class format2 {
public static void main(String args[]) {
int n = 5;
final int COUNT = 25000;
for (int i = 1; i <= COUNT; i++) {
String s = "The square of " + n + " is " + n * n + "n";
System.out.print(s);
}
}
}
方法 3
第三种方法使用java.text包中的 MessageFormat 类:
import java.text.*;
public class format3 {
public static void main(String args[]) {
MessageFormat fmt = new MessageFormat("The square of {0} is {1}n");
Object values[] = new Object[2];
int n = 5;
values[0] = new Integer(n);
values[1] = new Integer(n * n);
final int COUNT = 25000;
for (int i = 1; i <= COUNT; i++) {
String s = fmt.format(values);
System.out.print(s);
}
}
}
这些程序产生同样的输出。运行时间是:
format1 1.3 format2 1.8 format3 7.8
或者说最慢的和最快的大约是6比1。如果格式没有预编译第三种方法将更慢,使用静态的方法代替:
方法 4
MessageFormat.format(String, Object[])
import java.text.*;
public class format4 {
public static void main(String args[]) {
String fmt = "The square of {0} is {1}n";
Object values[] = new Object[2];
int n = 5;
values[0] = new Integer(n);
values[1] = new Integer(n * n);
final int COUNT = 25000;
for (int i = 1; i <= COUNT; i++) {
String s = MessageFormat.format(fmt, values);
System.out.print(s);
}
}
}
这比前一个例子多花费1/3的时间。
第三个方法比前两种方法慢很多的事实并不意味着你不应该使用它,而是你要意识到时间上的开销。
在国际化的情况下信息格式化是很重要的,关心这个问题的应用程序通常从一个绑定的资源中读取格式然后使用它。
918

被折叠的 条评论
为什么被折叠?



