格式化开销
实际上,将数据写入文件只是输出开销的一部分。另外一个巨大的开销是数据的格式 化。考虑下面的三个例 子,要求其输出如下的行:
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 25\n";
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。
方法3比1、2慢一点,并不意味不应该采用它。但是,应该明白在时间上的代价。
在国际化语言环境中,消息的格式是非常重要的,涉及到这个问题的应用程序通常从一个资源文件中读取该 格式,然后使用它。
随机存储
RandomAccessFile是用于对文件进行随机I/O存储(在字节层次上)的一个Java类。该类提供了一个与C/C++中 相似的搜索方法,以将文件指针移动到任意位置,然后就可以对从那里开始的字节进行读或写了。 该搜索方法访问底层的运行系统,正因为如此,开销可能非常昂贵。一个稍微廉价的替代方法是,在 RandomAccessFile顶部设置自己的缓冲,并且实现对字节的直接读取方法。用于读取的参数是所需字节的字节 偏移量。下面的例子显示了这是如何进行的:
import java.io.*;
public class ReadRandom {
private static final int DEFAULT_BUFSIZE = 4096;
private RandomAccessFile raf;
private byte inbuf[];
private long startpos = -1;
private long endpos = -1;
private int bufsize;
public ReadRandom(String name)
throws FileNotFoundException {
this(name, DEFAULT_BUFSIZE);
}
public ReadRandom(String name, int b)
throws FileNotFoundException {
raf = new RandomAccessFile(name, "r");
bufsize = b;
inbuf = new byte[bufsize];
}
public int read(long pos) {
if (pos < startpos || pos > endpos) {
long blockstart = (pos / bufsize) * bufsize;
int n;
try {
raf.seek(blockstart);
n = raf.read(inbuf);
}
catch (IOException e) {
return -1;
}
startpos = blockstart;
endpos = blockstart + n - 1;
if (pos < startpos || pos > endpos)
return -1;
}
return inbuf[(int)(pos - startpos)] & 0xffff;
}
public void close() throws IOException {
raf.close();
}
public static void main(String args[]) {
if (args.length != 1) {
System.err.println("missing filename");
System.exit(1);
}
try {
ReadRandom rr = new ReadRandom(args[0]);
long pos = 0;
int c;
byte buf[] = new byte[1];
while ((c = rr.read(pos)) != -1) {
pos++;
buf[0] = (byte)c;
System.out.write(buf, 0, 1);
}
rr.close();
}
catch (IOException e) {
System.err.println(e);
}
}
}
本驱动器程序简单地顺序读取字节,并且输出。
如果拥有存储的局部性(在文件中相邻位置的字节可以被同时读取),那么这项技术很有帮助。例如,如果 在已排序的文件中实现二叉树搜索算法,此方法可能很有用。如果在一个大文件中的任意位置进行随机存 储,其价值较小。