我正在尝试几种方法将文件的字节解码为字符。
使用 java.io.io.reader 和 channels.newreader(...)
public static void decodeWithReader() throws Exception {
FileInputStream fis = new FileInputStream(FILE);
FileChannel channel = fis.getChannel();
CharsetDecoder decoder = Charset.defaultCharset().newDecoder();
Reader reader = Channels.newReader(channel, decoder, -1);
final char[] buffer = new char[4096];
for(;;) {
if(-1 == reader.read(buffer)) {
break;
}
}
fis.close();
}
使用缓冲区和解码器手动:
public static void readWithBuffers() throws Exception {
FileInputStream fis = new FileInputStream(FILE);
FileChannel channel = fis.getChannel();
CharsetDecoder decoder = Charset.defaultCharset().newDecoder();
final long fileLength = channel.size();
long position = 0;
final int bufferSize = 1024 * 1024; // 1MB
CharBuffer cbuf = CharBuffer.allocate(4096);
while(position < fileLength) {
MappedByteBuffer bbuf = channel.map(MapMode.READ_ONLY, position, Math.min(bufferSize, fileLength - position));
for(;;) {
CoderResult res = decoder.decode(bbuf, cbuf, false);
if(CoderResult.OVERFLOW == res) {
cbuf.clear();
} else if (CoderResult.UNDERFLOW == res) {
break;
}
}
position += bbuf.position();
}
fis.close();
}
对于200MB文本文件,第一种方法始终需要300ms才能完成。第二种方法始终需要700毫秒。您是否知道为什么读者的方法要快得多?
它可以通过另一个实现更快地运行吗?
基准测试是在Windows 7上执行的,JDK7_07。
用于比较您可以尝试。
public static void readWithBuffersISO_8859_1() throws Exception {
FileInputStream fis = new FileInputStream(FILE);
FileChannel channel = fis.getChannel();
MappedByteBuffer bbuf = channel.map(FileChannel.MapMode.READ_ONLY, 0, channel.size());
while(bbuf.remaining()>0) {
char ch = (char)(bbuf.get() & 0xFF);
}
fis.close();
}
这是ISO-8859-1。如果您想要最大的速度,则将文本视为二进制格式,如果其选项可以帮助您。
正如@ejp指出的那样,您正在更改许多事情,您需要从最简单的可比较示例开始,看看每个元素添加了多少差异。
这是第三个实现,不使用映射的缓冲区。在相同的条件下,它在220毫秒内持续运行。我的计算机上的默认字符是" Windows-1252",如果我强制更简单的ISO-8859-1" charset解码甚至更快(约150ms)。
看起来像本机的用法诸如映射缓冲区之类的功能实际上会损害性能(对于这种用例)。同样有趣的是,如果我分配直接缓冲区而不是堆缓冲区(查看评论的线路),那么性能会降低(然后运行大约需要400ms)。
到目前为止的答案似乎是:在Java中尽可能快地解码字符(前提是您无法强制使用一个字符),手动使用解码器,用堆堆缓冲器编写解码循环,请勿使用映射的缓冲区甚至本地缓冲区。我必须承认我真的不知道为什么会这样。
public static void readWithBuffers() throws Exception {
FileInputStream fis = new FileInputStream(FILE);
FileChannel channel = fis.getChannel();
CharsetDecoder decoder = Charset.defaultCharset().newDecoder();
// CharsetDecoder decoder = Charset.forName("ISO-8859-1").newDecoder();
ByteBuffer bbuf = ByteBuffer.allocate(4096);
// ByteBuffer bbuf = ByteBuffer.allocateDirect(4096);
CharBuffer cbuf = CharBuffer.allocate(4096);
// CharBuffer cbuf = ByteBuffer.allocateDirect(2 * 4096).asCharBuffer();
for(;;) {
if(-1 == channel.read(bbuf)) {
decoder.decode(bbuf, cbuf, true);
decoder.flush(cbuf);
break;
}
bbuf.flip();
CoderResult res = decoder.decode(bbuf, cbuf, false);
if(CoderResult.OVERFLOW == res) {
cbuf.clear();
} else if (CoderResult.UNDERFLOW == res) {
bbuf.compact();
}
}
fis.close();
}