CharBuffer与char

有没有理由更喜欢CharBufferchar[] ,如下所示:

 CharBuffer buf = CharBuffer.allocate(DEFAULT_BUFFER_SIZE); while( in.read(buf) >= 0 ) { out.append( buf.flip() ); buf.clear(); } 

 char[] buf = new char[DEFAULT_BUFFER_SIZE]; int n; while( (n = in.read(buf)) >= 0 ) { out.write( buf, 0, n ); } 

(在哪里ReaderWriter )?

不,在这种情况下,没有理由更喜欢CharBuffer

但是,通常, CharBuffer (和ByteBuffer )可以真正简化API并鼓励正确处理。 如果您正在设计公共API,那么绝对值得考虑面向缓冲区的API。

我想对这个比较进行微型基准测试。

以下是我写的课程。

事情是我无法相信CharBuffer表现如此糟糕。 我有什么问题?

编辑:由于下面的第11条评论,我编辑了代码和输出时间,全面的性能,但仍然有很大的时间差异。 我还尝试了注释中提到的out2.append((CharBuffer)buff.flip())选项,但它比下面代码中使用的write选项慢得多。

结果:(以毫秒为单位)
char []:3411
CharBuffer:5653

 public class CharBufferScratchBox { public static void main(String[] args) throws Exception { // Some Setup Stuff String smallString = "1111111111222222222233333333334444444444555555555566666666667777777777888888888899999999990000000000"; StringBuilder stringBuilder = new StringBuilder(); for (int i = 0; i < 1000; i++) { stringBuilder.append(smallString); } String string = stringBuilder.toString(); int DEFAULT_BUFFER_SIZE = 1000; int ITTERATIONS = 10000; // char[] StringReader in1 = null; StringWriter out1 = null; Date start = new Date(); for (int i = 0; i < ITTERATIONS; i++) { in1 = new StringReader(string); out1 = new StringWriter(string.length()); char[] buf = new char[DEFAULT_BUFFER_SIZE]; int n; while ((n = in1.read(buf)) >= 0) { out1.write( buf, 0, n); } } Date done = new Date(); System.out.println("char[] : " + (done.getTime() - start.getTime())); // CharBuffer StringReader in2 = null; StringWriter out2 = null; start = new Date(); CharBuffer buff = CharBuffer.allocate(DEFAULT_BUFFER_SIZE); for (int i = 0; i < ITTERATIONS; i++) { in2 = new StringReader(string); out2 = new StringWriter(string.length()); int n; while ((n = in2.read(buff)) >= 0) { out2.write( buff.array(), 0, n); buff.clear(); } } done = new Date(); System.out.println("CharBuffer: " + (done.getTime() - start.getTime())); } } 

如果这是你用缓冲区做的唯一事情,那么在这个例子中,数组可能是更好的选择。

CharBuffer上有很多额外的铬,但在这种情况下它们都不相关 – 并且只会减慢一小部分。

如果你需要让事情变得更复杂,你可以随后重构。

在实践中,差异实际上<10%,而不是其他人报告的30%。

要读取和写入5MB文件24次,我的号码使用Profiler。 他们平均来说:

 char[] = 4139 ms CharBuffer = 4466 ms ByteBuffer = 938 (direct) ms 

个人测试几次赞成CharBuffer。

我还尝试用内存IO替换基于文件的IO,性能类似。 如果您尝试从一个本地流转移到另一个本地流,那么最好使用“直接”ByteBuffer。

性能差异小于10%,在实践中,我更青睐CharBuffer。 它的语法更清晰,外部变量更少,你可以对它进行更多的直接操作(即任何要求CharSequence的东西)。

基准测试低于……稍微有点错误,因为BufferedReader是在测试方法内而不是在外部分配的……但是,下面的示例允许您隔离IO时间并消除像字符串或字节流调整其内部大小等因素内存缓冲区等

 public static void main(String[] args) throws Exception { File f = getBytes(5000000); System.out.println(f.getAbsolutePath()); try { System.gc(); List
impls = new java.util.ArrayList
(); impls.add(new CharArrayImpl()); //impls.add(new CharArrayNoBuffImpl()); impls.add(new CharBufferImpl()); //impls.add(new CharBufferNoBuffImpl()); impls.add(new ByteBufferDirectImpl()); //impls.add(new CharBufferDirectImpl()); for (int i = 0; i < 25; i++) { for (Main impl : impls) { test(f, impl); } System.out.println("-----"); if(i==0) continue; //reset profiler } System.gc(); System.out.println("Finished"); return; } finally { f.delete(); } } static int BUFFER_SIZE = 1000; static File getBytes(int size) throws IOException { File f = File.createTempFile("input", ".txt"); FileWriter writer = new FileWriter(f); Random r = new Random(); for (int i = 0; i < size; i++) { writer.write(Integer.toString(5)); } writer.close(); return f; } static void test(File f, Main impl) throws IOException { InputStream in = new FileInputStream(f); File fout = File.createTempFile("output", ".txt"); try { OutputStream out = new FileOutputStream(fout, false); try { long start = System.currentTimeMillis(); impl.runTest(in, out); long end = System.currentTimeMillis(); System.out.println(impl.getClass().getName() + " = " + (end - start) + "ms"); } finally { out.close(); } } finally { fout.delete(); in.close(); } } public abstract void runTest(InputStream ins, OutputStream outs) throws IOException; public static class CharArrayImpl extends Main { char[] buff = new char[BUFFER_SIZE]; public void runTest(InputStream ins, OutputStream outs) throws IOException { Reader in = new BufferedReader(new InputStreamReader(ins)); Writer out = new BufferedWriter(new OutputStreamWriter(outs)); int n; while ((n = in.read(buff)) >= 0) { out.write(buff, 0, n); } } } public static class CharBufferImpl extends Main { CharBuffer buff = CharBuffer.allocate(BUFFER_SIZE); public void runTest(InputStream ins, OutputStream outs) throws IOException { Reader in = new BufferedReader(new InputStreamReader(ins)); Writer out = new BufferedWriter(new OutputStreamWriter(outs)); int n; while ((n = in.read(buff)) >= 0) { buff.flip(); out.append(buff); buff.clear(); } } } public static class ByteBufferDirectImpl extends Main { ByteBuffer buff = ByteBuffer.allocateDirect(BUFFER_SIZE * 2); public void runTest(InputStream ins, OutputStream outs) throws IOException { ReadableByteChannel in = Channels.newChannel(ins); WritableByteChannel out = Channels.newChannel(outs); int n; while ((n = in.read(buff)) >= 0) { buff.flip(); out.write(buff); buff.clear(); } } }

我认为CharBuffer和ByteBuffer(以及任何其他xBuffer)都是为了可重用性,所以你可以buf.clear()它们而不是每次重新分配

如果你不重复使用它们,你就没有充分利用它们,它会增加额外的开销。 但是,如果您计划扩展此function,那么将它们保留在那里可能是一个好主意

CharBuffer版本稍微复杂一点(少一个变量),封装缓冲区大小处理并使用标准API。 一般来说我更喜欢这个。

然而,至少在某些情况下,仍然有一个很好的理由更喜欢arrays版本。 CharBuffer仅在Java 1.4中引入,因此如果您要部署到早期版本, 则不能使用Charbuffer(除非您自己角色/使用backport)。

PS如果您使用backport,请记住,一旦您赶上包含反向移植代码的“真实”版本的版本,就将其删除。

你应该在最近的Java版本中避免使用CharBuffer#subsequence()有一个错误。 您无法从缓冲区的后半部分获取子序列,因为实现会混淆capacityremaining capacity 。 我在java 6-0-11和6-0-12中观察到了这个bug。