在Java核心技术卷Ⅱ(原书第九版) 1.7节比较了几种文件操作时间,结论是内存映射文件速度明显快于随机访问文件速度。为何随机访问会慢20倍左右?写个程序验证一下。
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
public class Test {
// InputStream 和 RandomAccessFile 方法速度太慢, 只读取部分数据
static int MAX_COUNT = 1024 * 1024;
static void checkInputStream(Path fileName) throws IOException {
long start = System.currentTimeMillis();
long sum = 0L;
try (InputStream in = Files.newInputStream(fileName)) {
int c, cnt = 0;
while ((c = in.read()) != -1) {
sum += c;
if (++cnt == MAX_COUNT)
break;
}
}
long end = System.currentTimeMillis();
print(String.format("InputStream(%dKb)", MAX_COUNT / 1024), sum, end - start);
}
static void checkBufferedInputStream(Path fileName) throws IOException {
long start = System.currentTimeMillis();
long sum = 0L;
try (InputStream in = new BufferedInputStream(Files.newInputStream(fileName))) {
int c;
while ((c = in.read()) != -1)
sum += c;
}
long end = System.currentTimeMillis();
print("BufferedInputStream", sum, end - start);
}
static void checkRandomAccessFile(Path fileName) throws IOException {
long start = System.currentTimeMillis();
long sum = 0L;
try (RandomAccessFile in = new RandomAccessFile(fileName.toFile(), "r")) {
int c, cnt = 0;
while ((c = in.read()) != -1) {
sum += c;
if (++cnt == MAX_COUNT)
break;
}
}
long end = System.currentTimeMillis();
print(String.format("RandomAccessFile(%dKb)", MAX_COUNT / 1024), sum, end - start);
}
static void checkBufferedRandomAccessFile(Path fileName, int bufSize) throws IOException {
long start = System.currentTimeMillis();
long sum = 0L;
try (RandomAccessFile in = new RandomAccessFile(fileName.toFile(), "r")) {
byte[] buf = new byte[bufSize];
int count;
while ((count = in.read(buf)) > 0) {
for (int i = 0; i < count; i++) {
sum += buf[i] & 0xFF;
}
if (count != bufSize) {
break;
}
}
}
long end = System.currentTimeMillis();
print(String.format("Buffered(%d) RandomAccessFile", bufSize), sum, end - start);
}
static void checkMappedFile(Path fileName) throws IOException {
long start = System.currentTimeMillis();
long sum = 0L;
try (FileChannel channel = FileChannel.open(fileName)) {
int length = (int) channel.size();
MappedByteBuffer buffer = channel.map(FileChannel.MapMode.READ_ONLY, 0, length);
for (int i = 0; i < length; i++) {
int c = buffer.get(i) & 0xFF;
sum += c;
}
}
long end = System.currentTimeMillis();
print("MappedFile", sum, end - start);
}
static void print(String method, long sum, long time) throws IOException {
System.out.printf("%32s: %12d %6d\n", method, sum, time);
}
public static void main(String[] args) throws IOException {
checkInputStream(Paths.get("D:\\Txx728-1"));
checkBufferedInputStream(Paths.get("D:\\Txx728-2"));
checkRandomAccessFile(Paths.get("D:\\Txx728-3"));
checkBufferedRandomAccessFile(Paths.get("D:\\Txx728-4"), 4096);
checkBufferedRandomAccessFile(Paths.get("D:\\Txx728-5"), 4096 * 2);
checkMappedFile(Paths.get("D:\\Txx728-6"));
}
}
实际运行结果:
说明:为避免系统缓存文件,把一个37M的文件复制多份分别测试。由于 InputStream 和 RandomAccessFile 方法速度太慢, 只读取部分数据进行测试。
从运行数据可以看出
- InputStream 与 RandomAccessFile 基本相当。
- BufferedInputStream 要比 InputStream 快两个量级。
- 操作系统的文件缓存对结果有很大影响。
- 带缓冲的 RandomAccessFile 与 MappedFile 基本相当。